如何实现 __getattribute__ 而不出现无限递归错误?
我想重写对类中一个变量的访问,但正常返回所有其他变量。 如何使用 __getattribute__ 来实现此目的?
我尝试了以下操作(这也应该说明我正在尝试做什么),但出现递归错误:
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
I want to override access to one variable in a class, but return all others normally. How do I accomplish this with __getattribute__
?
I tried the following (which should also illustrate what I'm trying to do) but I get a recursion error:
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
您会收到递归错误,因为您尝试访问
__getattribute__
内的self.__dict__
属性会再次调用__getattribute__
。 如果您使用object
的__getattribute__
来代替,它会起作用:这是有效的,因为
object
(在本例中)是基类。 通过调用 __getattribute__ 的基本版本,您可以避免之前陷入的递归地狱。foo.py 中代码的 Ipython 输出:
更新:
标题为 当前文档中对新样式类的更多属性访问,他们建议这样做以避免无限递归。
You get a recursion error because your attempt to access the
self.__dict__
attribute inside__getattribute__
invokes your__getattribute__
again. If you useobject
's__getattribute__
instead, it works:This works because
object
(in this example) is the base class. By calling the base version of__getattribute__
you avoid the recursive hell you were in before.Ipython output with code in foo.py:
Update:
There's something in the section titled More attribute access for new-style classes in the current documentation, where they recommend doing exactly this to avoid the infinite recursion.
实际上,我相信您想使用
__getattr__
改为特殊方法。引用Python文档:
注意:要使其正常工作,实例不应具有
test
属性,因此应删除self.test=20
行。Actually, I believe you want to use the
__getattr__
special method instead.Quote from the Python docs:
Note: for this to work, the instance should not have a
test
attribute, so the lineself.test=20
should be removed.Python 语言参考:
含义:
您正在调用一个名为
__dict__
的属性。 因为它是一个属性,所以在搜索__dict__
时调用__getattribute__
,它调用__getattribute__
,它调用 ... yada yada yada使用基类
__getattribute__
有助于找到真实的属性。Python language reference:
Meaning:
You're calling for an attribute called
__dict__
. Because it's an attribute,__getattribute__
gets called in search for__dict__
which calls__getattribute__
which calls ... yada yada yadaUsing the base classes
__getattribute__
helps finding the real attribute.它在正常的点查找之前调用。 如果它引发
AttributeError
,那么我们调用__getattr__
。这种方法的使用相当罕见。 标准库中只有两个定义:
最佳实践
以编程方式控制对单个属性的访问的正确方法是使用
属性
。 类D
应该写成如下(可以使用设置器和删除器来复制明显的预期行为):用法:
属性是一个数据描述符,因此它是在正常的点线中查找的第一个东西查找算法。
__getattribute__
的选项如果您确实需要通过
__getattribute__
。AttributeError
,导致__getattr__
被调用(如果实现)super
调用父级 (可能是对象
的)实现__getattr__
例如:
您最初想要的。
这个例子展示了您如何做您最初想要的事情:
并且将表现得像这样:
代码审查
您的带有注释的代码。 您在
__getattribute__
中对 self 进行了点式查找。这就是您收到递归错误的原因。 您可以检查 name 是否为
"__dict__"
并使用super
来解决方法,但这不包括__slots__
。 我将把它作为练习留给读者。It is called before the normal dotted lookup. If it raises
AttributeError
, then we call__getattr__
.Use of this method is rather rare. There are only two definitions in the standard library:
Best Practice
The proper way to programmatically control access to a single attribute is with
property
. ClassD
should be written as follows (with the setter and deleter optionally to replicate apparent intended behavior):And usage:
A property is a data descriptor, thus it is the first thing looked for in the normal dotted lookup algorithm.
Options for
__getattribute__
You several options if you absolutely need to implement lookup for every attribute via
__getattribute__
.AttributeError
, causing__getattr__
to be called (if implemented)super
to call the parent (probablyobject
's) implementation__getattr__
For example:
What you originally wanted.
And this example shows how you might do what you originally wanted:
And will behave like this:
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 usesuper
to workaround, but that doesn't cover__slots__
. I'll leave that as an exercise to the reader.您确定要使用
__getattribute__
吗? 你到底想达到什么目的?执行您要求的最简单方法是:
或:
编辑:
请注意,
D
的实例在每种情况下都会具有不同的test
值。 在第一种情况下,d.test
将为 20,在第二种情况下,它将为 0。我将让您找出原因。编辑2:
Greg 指出示例 2 将失败,因为该属性是只读的,并且 __init__ 方法尝试将其设置为 20。更完整的示例是:
显然,作为一个类,这几乎完全是无用,但它给了你一个继续前进的想法。
Are you sure you want to use
__getattribute__
? What are you actually trying to achieve?The easiest way to do what you ask is:
or:
Edit:
Note that an instance of
D
would have different values oftest
in each case. In the first cased.test
would be 20, in the second it would be 0. I'll leave it to you to work out why.Edit2:
Greg pointed out that example 2 will fail because the property is read only and the
__init__
method tried to set it to 20. A more complete example for that would be:Obviously, as a class this is almost entirely useless, but it gives you an idea to move on from.
这是一个更可靠的版本:
它从父类调用 __getattribute__ 方法,最终返回到对象。__getattribute__ 方法(如果其他祖先没有覆盖它)。
Here is a more reliable version:
It calls __getattribute__ method from parent class, eventually falling back to object.__getattribute__ method if other ancestors don't override it.