How is the __getattribute__
method used?
它在正常的点查询之前被称为。如果它引发AttributeError
,那么我们调用__getattr__
。
使用此方法相对较少。在标准库中仅有两个定义:
$ grep -Erl "def __getattribute__(self" cpython/Lib | grep -v "/test/"
cpython/Lib/_threading_local.py
cpython/Lib/importlib/util.py
最佳实践
以编程方式控制对单个属性的访问的正确方法是使用 property
。类D
应该按以下方式编写(附带 setter 和 deleter 可以复制所需的行为):
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
一种属性是数据描述符,因此它是正常点查找算法中首先查找的东西。
Options for __getattribute__
如果您绝对需要通过__getattribute__
实现每个属性的查找,则有几个选项。
- raise
AttributeError
, causing __getattr__
to be called (if implemented)
- return something from it by
- using
super
to call the parent (probably object
s) implementation
- calling
__getattr__
- implementing your own dotted lookup algorithm somehow
例如:
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
What you originally wanted.
这个例子展示了你如何做到最初想要的事情:
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
Code review
Your code with comments. You have a dotted lookup on self in __getattribute__
.
This is why you get a recursion error. You could check if name is "__dict__"
and use super
to workaround, but that doesn t cover __slots__
. I ll leave that as an exercise to the reader.
class D(object):
def __init__(self):
self.test=20
self.test2=21
def __getattribute__(self,name):
if name== test :
return 0.
else: # v--- Dotted lookup on self in __getattribute__
return self.__dict__[name]
>>> print D().test
0.0
>>> print D().test2
...
RuntimeError: maximum recursion depth exceeded in cmp