如何使用__getattribute__
方法?
在正常的点语法查找之前调用此方法。如果引发AttributeError
异常,则会调用__getattr__
方法。
这种方法的使用相当罕见。标准库中只有两个定义:
$ grep -Erl "def __getattribute__\(self" cpython/Lib | grep -v "/test/"
cpython/Lib/_threading_local.py
cpython/Lib/importlib/util.py
最佳实践
通过property
函数来编写程序控制单个属性的正确方式。类D
应按以下方式编写(设置器和删除器可选地复制表面上预期的行为):
class D(object):
def __init__(self):
self.test2=21
@property
def test(self):
return 0.
@test.setter
def test(self, value):
'''dummy function to avoid AttributeError on setting property'''
@test.deleter
def test(self):
'''dummy function to avoid AttributeError on deleting property'''
使用方法:
>>> o = D()
>>> o.test
0.0
>>> o.test = 'foo'
>>> o.test
0.0
>>> del o.test
>>> o.test
0.0
属性是数据描述符,因此在正常的点查找算法中首先查找。
__getattribute__
的选项
如果您绝对需要通过__getattribute__
实现每个属性的查找,则有以下几种选项。
- 引发
AttributeError
,从而调用__getattr__
(如果已实现)
- 通过以下方式之一从其中返回内容:
- 使用
super
调用父级(可能是object
的)实现
- 调用
__getattr__
- 以某种方式实现自己的点查找算法
例如:
class NoisyAttributes(object):
def __init__(self):
self.test=20
self.test2=21
def __getattribute__(self, name):
print('getting: ' + name)
try:
return super(NoisyAttributes, self).__getattribute__(name)
except AttributeError:
print('oh no, AttributeError caught and reraising')
raise
def __getattr__(self, name):
"""Called if __getattribute__ raises AttributeError"""
return 'close but no ' + name
>>> n = NoisyAttributes()
>>> nfoo = n.foo
getting: foo
oh no, AttributeError caught and reraising
>>> nfoo
'close but no foo'
>>> n.test
getting: test
20
你最初想要的是什么
以下示例展示了如何实现你最初想要的内容:
class D(object):
def __init__(self):
self.test=20
self.test2=21
def __getattribute__(self,name):
if name=='test':
return 0.
else:
return super(D, self).__getattribute__(name)
并且将会表现得像这样:
>>> o = D()
>>> o.test = 'foo'
>>> o.test
0.0
>>> del o.test
>>> o.test
0.0
>>> del o.test
Traceback (most recent call last):
File "<pyshell#216>", line 1, in <module>
del o.test
AttributeError: test
代码评审
您的代码附有注释。您在__getattribute__
中使用了self的点式查找,这就是为什么会出现递归错误的原因。您可以检查name是否为"__dict__"
并使用super
进行解决,但这并不能涵盖__slots__
。我将把这留给读者作为练习。
class D(object):
def __init__(self):
self.test=20
self.test2=21
def __getattribute__(self,name):
if name=='test':
return 0.
else:
return self.__dict__[name]
>>> print D().test
0.0
>>> print D().test2
...
RuntimeError: maximum recursion depth exceeded in cmp
super(D, self).__getattribute__(name)
- gepatinosuper().__getattribute__(name)
来实现该功能。 - jeromej