问题:了解__getattr__和__getattribute__之间的区别
我试图理解上的差异之间__getattr__
和__getattribute__
,但是,我在它失败。
堆栈溢出问题的答案与vs 之间的区别是__getattr__
__getattribute__
:
__getattribute__
在查看对象的实际属性之前调用,因此很难正确实现。您可以非常轻松地进行无限递归。
我完全不知道那是什么意思。
然后继续说:
您几乎可以肯定想要
__getattr__
。
为什么?
我读到,如果__getattribute__
失败,__getattr__
则称为。那么,为什么有两种不同的方法做同样的事情呢?如果我的代码实现了新样式类,我应该使用什么?
我正在寻找一些代码示例来清除此问题。我已尽我所能搜索Google,但是我发现的答案并未彻底讨论该问题。
如果有任何文档,我准备阅读。
回答 0
首先了解一些基础知识。
对于对象,您需要处理其属性。通常我们会这么做instance.attribute
。有时我们需要更多的控制权(当我们事先不知道属性名称时)。
例如,instance.attribute
将变为getattr(instance, attribute_name)
。使用此模型,我们可以通过提供attribute_name作为字符串来获取属性。
用于 __getattr__
您还可以告诉类如何处理它未显式管理的属性,并通过__getattr__
方法进行操作。
每当您请求尚未定义的属性时,Python都会调用此方法,因此您可以定义该方法。
一个经典的用例:
class A(dict):
def __getattr__(self, name):
return self[name]
a = A()
# Now a.somekey will give a['somekey']
注意事项和使用 __getattribute__
如果您需要捕获每个属性(无论是否存在),请使用__getattribute__
。不同之处在于,__getattr__
仅调用实际上不存在的属性。如果您直接设置属性,则引用该属性将无需调用即可检索它__getattr__
。
__getattribute__
一直被称为。
回答 1
__getattribute__
每当发生属性访问时都会调用。
class Foo(object):
def __init__(self, a):
self.a = 1
def __getattribute__(self, attr):
try:
return self.__dict__[attr]
except KeyError:
return 'default'
f = Foo(1)
f.a
这将导致无限递归。罪魁祸首是排队return self.__dict__[attr]
。让我们假装(这与事实很接近)所有属性都存储在self.__dict__
名称中并可用。线
f.a
尝试访问的a
属性f
。这叫f.__getattribute__('a')
。__getattribute__
然后尝试加载self.__dict__
。__dict__
是的属性,self == f
因此python调用f.__getattribute__('__dict__')
再次尝试访问属性'__dict__
‘。这是无限递归。
如果__getattr__
曾经使用过,那么
- 它永远不会运行,因为
f
具有a
属性。 - 如果它已经运行((假设您要
f.b
)),则不会调用__dict__
它,因为它已经存在,并且__getattr__
仅当所有其他查找属性的方法均失败时才被调用。
编写上述类的“正确”方法__getattribute__
是
class Foo(object):
# Same __init__
def __getattribute__(self, attr):
return super(Foo, self).__getattribute__(attr)
super(Foo, self).__getattribute__(attr)
将__getattribute__
“最近”超类的方法(self
通常是该类的“方法解析顺序”中的下一个类)绑定到当前对象,然后调用它并让其完成工作。
通过使用__getattr__
python 可以在没有找到属性之前将其正常处理,从而避免了所有这些麻烦。到那时,Python将控制权交给您的__getattr__
方法,并让它提出一些建议。
还值得注意的是,您可以使用进行无限递归__getattr__
。
class Foo(object):
def __getattr__(self, attr):
return self.attr
我将把它留作练习。
回答 2
我认为其他的答案做了解释之间的差异的一个伟大的工作__getattr__
和__getattribute__
,但有一点可能没有明确的是,为什么你会想使用__getattribute__
。有趣的__getattribute__
是,它本质上允许您在访问类时重载点。这使您可以自定义如何在较低级别访问属性。例如,假设我要定义一个类,其中所有仅带有自变量的方法都被视为属性:
# prop.py
import inspect
class PropClass(object):
def __getattribute__(self, attr):
val = super(PropClass, self).__getattribute__(attr)
if callable(val):
argcount = len(inspect.getargspec(val).args)
# Account for self
if argcount == 1:
return val()
else:
return val
else:
return val
从交互式解释器中:
>>> import prop
>>> class A(prop.PropClass):
... def f(self):
... return 1
...
>>> a = A()
>>> a.f
1
当然,这是一个愚蠢的示例,您可能永远也不想这样做,但是它向您展示了从覆盖获得的强大功能__getattribute__
。
回答 3
我经历了别人的出色解释。但是,我从此博客Python Magic Methods和__getattr__
找到了一个简单的答案。以下所有都是从那里开始的。
使用__getattr__
magic方法,我们可以拦截不存在的属性查找并做一些事情,以确保它不会失败:
class Dummy(object):
def __getattr__(self, attr):
return attr.upper()
d = Dummy()
d.does_not_exist # 'DOES_NOT_EXIST'
d.what_about_this_one # 'WHAT_ABOUT_THIS_ONE'
但是,如果该属性确实存在,__getattr__
将不会被调用:
class Dummy(object):
def __getattr__(self, attr):
return attr.upper()
d = Dummy()
d.value = "Python"
print(d.value) # "Python"
__getattribute__
与相似__getattr__
,但重要的区别是__getattribute__
将拦截每个属性查找,而属性是否存在无关紧要。
class Dummy(object):
def __getattribute__(self, attr):
return 'YOU SEE ME?'
d = Dummy()
d.value = "Python"
print(d.value) # "YOU SEE ME?"
在该示例中,d
对象已经具有属性值。但是,当我们尝试访问它时,没有得到原始的期望值(“ Python”);我们只是得到任何__getattribute__
回报。这意味着我们实际上失去了value属性;它已经变得“无法到达”。