动态创建指向属性属性的属性

时间:2018-07-02 11:54:51

标签: python oop properties

我想使类的属性的属性可以直接通过类的实例访问(而不从其继承)。所以基本上,如果我有:

class A:
    @property
    def foo(self):
        print("foo")
    @property
    def bar(self):
        print("bar")

class B:
    def __init__(self):
        self._a = A()

我希望能够做b._a.bar,而不是做b.bar。基于this answer here,我在B类中尝试了以下方法:

class B:
    def __init__(self):
        self._a = A()
        attrs = [attr for attr in dir(self._a) 
                 if not callable(self._a.__getattribute__(attr)) 
                 and not attr.startswith("__")]
        for attr in attrs:
            setattr(self.__class__, attr, 
                    property(lambda s: s._a.__getattribute__(attr)))

但是当实例化和测试它时,我得到了这些怪异的python时刻之一:

>>> b = B()
foo
bar
>>> b.foo
bar
>>> b.bar
bar
  1. 为什么在创建实例时同时打印出'foo'和'bar'?
  2. 'foo'属性如何指向与'bar'相同的吸气剂?

1 个答案:

答案 0 :(得分:2)

创建实例时会打印

barfoo,因为执行_a.__getattribue__("foo")_a.foo都会调用属性对象来获取值。

您在B中设置的两个属性都使用lambda来从property获取正确的A。调用lambda时,这是一个常见错误。由于attr值是从外部范围继承的,因此在评估lambda时不会冻结它。相反,它只是与封闭范围的attr相同的attr引用,并相应地进行了更改。因此,您所有的lambda都将具有相同的attr值。

您可以改为定义B.__getattr__方法。普通属性查找失败时将调用此方法。

class B:
    def __init__(self):
        self._a = A()
    def __getattr__(self, name):
        return getattr(self._a, name)

b = B()
b.bar
# bar
b.foo
# foo