我正在学习python
的继承,并且遇到了一个我不太理解的行为。下面是一个最小的工作示例:
class Test():
def meth1(self):
print('accessing meth1')
return super().a #calling random nonexisting attribute; error (as expected)
@property
def prop1(self):
print('accessing prop1')
return super().a #calling random nonexisting attribute; no error?
def __getattr__(self, name):
print('getattr ' + name)
test = Test()
呼叫.meth1()
失败…
In [1]: test.meth1()
accessing meth1
Traceback (most recent call last):
File "<ipython-input-160-4a0675c95211>", line 1, in <module>
test.meth1()
File "<ipython-input-159-1401fb9a0e13>", line 5, in meth1
return super().a #calling random nonexisting attribute; error (as expected)
AttributeError: 'super' object has no attribute 'a'
…因为super()
是object
,它确实没有这个属性。
但.prop1
没有…
In [2]: test.prop1
accessing prop1
getattr prop1
…我不明白似乎该属性被调用两次,一次是"正常",一次是通过__getattr__
。
一些观察:
- 我想这与
property
装饰器有关。 - 属性
.a
似乎永远不会被访问。 - 如果我用
return 5
之类的东西替换prop1
中的return super().a
行,则永远不会调用__getattr__
方法。 - 如果我实际上使
Test
从具有属性a
的类继承,其值从test.meth1()
返回,而不是从test.prop1
返回。
谁能解释一下这是怎么回事?我没能找到任何有用的信息来解决属性装饰器和super()
的组合。
许多谢谢,
TLDR:meth1
在查找之后引发AttributeError
,此时不涉及__getattr__
。prop1
在查找时引发AttributeError
,触发回退到__getattr__
,并成功返回None
。
>>> test.prop1 # AttributeError happens here during lookup
accessing prop1
getattr prop1
>>> meth = test.meth1 # AttributeError happens *not* here during lookup
>>> meth() # AttributeError happens here *after* lookup
...
AttributeError: 'super' object has no attribute 'a'
__getattr__
方法只在"未找到"属性时调用。-换句话说,AttributeError
在访问时被引发。当该属性直接引发错误时,也会发生相同的行为:
class Test():
@property
def prop1(self):
print('accessing prop1')
raise AttributeError # replaces `super().a`
def __getattr__(self, name):
print('getattr ' + name)
test = Test()
test.prop1 # < explicitly raises AttributeError
# accessing prop1
# getattr prop1
test.prop2 # < implicitly raises AttributeError
# getattr prop2
AttributeError
不显示它是来自缺失的prop1
属性还是一些嵌套的内部属性(例如super().a
)。因此,两者都触发回退到__getattr__
。
这是__getattr__
的预期行为。
object.__getattr__(self, name)
当默认属性访问失败并产生AttributeError时调用(
__getattribute__()
抛出AttributeError
,因为name不是实例属性或self的类树属性;或__get__()
的name属性会引发AttributeError ()。
当属性不能产生值时,它允许属性回退到常规查找机制。