1

我有以下(显然是简化的)类

class A(object)
    def __init__(self, a):
        self.a = a
        self.b = 'b'
        # Other class attributes are added


class B(list):
    """
    Some customization of this class...
    """
    pass

BB = B([A(i) for i in range(10)])

我想要做

B.a

aB. 我知道为了做到这一点,我需要覆盖__getattr__,但我不确定实现这一点的最佳方式。这需要是通用的,因为B不知道A可能需要访问的任何属性。

有人可以就这个想法的实施提供一些建议吗?

4

1 回答 1

5

一般解决方案:

如果您希望它全面工作,那么您可以__getattr__()按照您的想法覆盖:

class A(object):
    def __init__(self, a):
        self.a = a
        self.b = a-1

class B(list):
    """
    Some customization of this class...
    """
    def __getattr__(self, name):
        return (getattr(item, name) for item in self)

bb = B([A(i) for i in range(10)])
print(list(bb.a))
print(list(bb.b))

给我们:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[-1, 0, 1, 2, 3, 4, 5, 6, 7, 8]

请注意,__getattr__()只有在属性不存在时才会调用。所以,如果你设置bb.b为另一个值,你会得到它:

bb = B([A(i) for i in range(10)])
bb.b = 5
print(list(bb.a))
print(bb.b)

给我们:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
5

显示不需要B了解其内容的示例:

>>> import datetime
>>> b = B([datetime.date(2012, 1, 1), datetime.date(2012, 2, 2), datetime.date(2012, 3, 3)])
>>> list(b.month)
[1, 2, 3]

原答案:

最简单的方法是使用生成器表达式

class B(list):
    """
    Some customization of this class...
    """
@property
def a(self):
    return (item.a for item in self)

这个生成器表达式等价于:

@property
def a(self):
    for item in self:
        yield item.a

我还使用内置property()函数作为装饰器,使其B.a充当属性而不是函数。

然后我们可以这样做:

bb = B([A(i) for i in range(10)])
print(list(bb.a))

并得到:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

如果您确实想要一个列表而不是迭代器,则可以使用列表推导([item.a for item in self]),但通常迭代器更有用,并且可以轻松制成列表(如上所示)。

请注意,您还可以通过分配生成器表达式来更简单地执行此操作:

class B(list):
        """
        Some customization of this class...
        """
    def __init__(self, *args):
        super(B, self).__init__(*args)
        self.a = (item.a for item in self)

但是,这意味着发电机在第一次使用后就会耗尽,所以我建议不要这样做。

于 2012-04-27T13:45:33.733 回答