获取容器python中所有项的属性

jlc*_*lin 1 python

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

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)])
Run Code Online (Sandbox Code Playgroud)

我想要做

B.a
Run Code Online (Sandbox Code Playgroud)

并从中获取a每个包含项目的所有属性的列表B.我知道为了做到这一点,我需要覆盖__getattr__,但我不确定实现它的最佳方法.这需要是通用的,因为B不知道A可能需要访问的任何属性.

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

Gar*_*tty 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))
Run Code Online (Sandbox Code Playgroud)

给我们:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[-1, 0, 1, 2, 3, 4, 5, 6, 7, 8]
Run Code Online (Sandbox Code Playgroud)

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

bb = B([A(i) for i in range(10)])
bb.b = 5
print(list(bb.a))
print(bb.b)
Run Code Online (Sandbox Code Playgroud)

给我们:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
5
Run Code Online (Sandbox Code Playgroud)

示例显示不需要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]
Run Code Online (Sandbox Code Playgroud)

原答案:

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

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

这个生成器表达式相当于:

@property
def a(self):
    for item in self:
        yield item.a
Run Code Online (Sandbox Code Playgroud)

我还使用property()内置的装饰,使B.a充当一个属性,而不是一个功能.

然后我们可以这样做:

bb = B([A(i) for i in range(10)])
print(list(bb.a))
Run Code Online (Sandbox Code Playgroud)

得到:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Run Code Online (Sandbox Code Playgroud)

如果你肯定想要一个列表而不是一个迭代器,你可以使用列表推导([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)
Run Code Online (Sandbox Code Playgroud)

但是,这意味着发电机在首次使用后会耗尽,所以我会反对它.