python中非常奇怪的__getattr__行为

Cra*_*sta 2 python python-2.7

我的代码中有一个错误(类似于下面的错误),但是结果对我来说似乎很奇怪。有人可以给我解释一下名称为“ hello”的getattr是如何被调用的吗?

class A(object):
    def __getattr__(self, name):
        print name
        assert name != 'hello'
        return self.hello

class C(object):
    def __init__(self):
        class B(A):
            @property
            def hello(self_):
                return self.durp

        self.b = B()

c = C()
print c.b.bang
Run Code Online (Sandbox Code Playgroud)

输出为:

bang
hello
Traceback (most recent call last):
  File "test.py", line 17, in <module>
    print c.b.bang
  File "test.py", line 5, in __getattr__
    return self.hello
  File "test.py", line 4, in __getattr__
    assert name != 'hello'
AssertionError

shell returned 1
Run Code Online (Sandbox Code Playgroud)

我当然知道C中没有durp属性(这是我所说的错误)。但是,我不希望__getattr__用hello作为字符串来调用它。我觉得我不了解有关__getattr__工作方式的基本知识。

Bre*_*arn 5

__getattr__通过“常用方法”访问属性失败时调用。Python知道“常规方法”是否失败的方式是引发AttributeError。但是,它无法确定是在尝试获取要获取的原始属性时引发了AttributeError,还是在该过程中访问了某些其他属性。所以这是发生了什么:

  1. Python尝试访问 c.b.bang
  2. b对象没有属性“ bang”,因此__getattr__称为。
  3. __getattr__试图得到self.hello
  4. Python找到属性hello,其值为属性
  5. 在尝试评估时self.hello,Python运行该hello属性。
  6. 运行该hello属性会尝试访问一个不存在的属性durp。这引发了AttributeError
  7. 由于在尝试评估时引发了AttributeError self.hello,Python认为通过“通常的方法”没有可用的此类属性。因此__getattr__,它以“ hello”作为参数进行调用。

这种行为令人困惑,但是从根本上来说,这是Python中属性查找如何工作的不可避免的副作用。知道“常规属性查找无效”的唯一方法是引发了AttributeError。有一个关于此的Python错误,显然人们仍在研究,但到目前为止尚无真正的解决方案。