【问题标题】:Creating properties dynamically that point to an attribute's properties动态创建指向属性属性的属性
【发布时间】:2018-12-10 15:34:30
【问题描述】:

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

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' 相同的 getter?

【问题讨论】:

标签: python oop properties


【解决方案1】:

barfoo 在创建实例时打印,因为执行 _a.__getattribue__("foo")_a.foo 都会调用属性对象来获取值。

您在 B 中设置的两个属性都使用 lambda 从 A 获取正确的 property。这是调用 lambdas 时的常见错误。因为 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

【讨论】:

  • 感谢您的回答和其他问题的链接!我想过定义__getattr__,但在我看来它会覆盖标准属性访问器。仅在标准访问器失败时才调用它似乎是合乎逻辑的(至少对我来说很方便)。它最终也比我最初的尝试更干净。直到 lambdas 很棒,但可能会产生危险的误导......
猜你喜欢
  • 2020-01-08
  • 1970-01-01
  • 1970-01-01
  • 2022-06-24
  • 2019-05-02
  • 1970-01-01
  • 1970-01-01
  • 2019-08-11
相关资源
最近更新 更多