我想了解__getattr__
和__getattribute__
之间的区别,但是,我没有做到这一点。
Stack Overflow问题的answer Difference between __getattr__
vs __getattribute__
说:
在查看实际属性之前调用
__getattribute__
对象,所以可能很棘手 正确实施。你最终可以进入 无限递归非常容易。
我完全不知道这意味着什么。
然后继续说:
你几乎肯定想要
__getattr__
。
为什么呢?
我读过如果__getattribute__
失败,则会调用__getattr__
。那么为什么有两种不同的方法做同样的事情呢?如果我的代码实现了新的样式类,我应该使用什么?
我正在寻找一些代码示例来清除这个问题。我用Google搜索了我的能力,但我找到的答案并没有彻底讨论这个问题。
如果有任何文件,我准备阅读。
答案 0 :(得分:263)
使用对象,您需要处理其属性。通常我们会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 :(得分:84)
__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
的{{1}}属性。这会调用f
。 f.__getattribute__('a')
然后尝试加载__getattribute__
。 self.__dict__
是__dict__
的属性,因此python调用self == f
再次尝试访问属性f.__getattribute__('__dict__')
'。这是无限递归。
如果使用了'__dict__
,那么
__getattr__
具有f
属性。a
)那么它就不会被调用来查找f.b
,因为它已经在那里,只有当<{1}}被调用时才会被调用em>查找属性的所有其他方法都失败。 使用__dict__
编写上述类的“正确”方法是
__getattr__
__getattribute__
将“最近的”超类的class Foo(object):
# Same __init__
def __getattribute__(self, attr):
return super(Foo, self).__getattribute__(attr)
方法(正式地,类的方法解析顺序中的下一个类,或MRO)绑定到当前对象super(Foo, self).__getattribute__(attr)
,然后调用它让我们开展工作。
通过使用__getattribute__
可以避免所有这些麻烦,这会让Python 做正常的事情,直到找不到属性。那时,Python会控制你的self
方法并让它产生一些东西。
值得注意的是,您可以使用__getattr__
进行无限递归。
__getattr__
我会留下那个作为练习。
答案 2 :(得分:58)
我认为其他答案在解释__getattr__
和__getattribute__
之间的差异方面做得很好,但有一点可能不太清楚,那就是为什么你要使用__getattribute__
。关于__getattribute__
的一个很酷的事情是它实际上允许你在访问类时重载点。这允许您自定义如何在较低级别访问属性。例如,假设我想定义一个类,其中所有仅采用self参数的方法都被视为属性:
# 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 :(得分:7)
我经历了别人的出色解释。但是,我从此博客Python Magic Methods and __getattr__
找到了一个简单的答案。以下所有都是从那里开始的。
使用__getattr__
魔术方法,我们可以拦截不存在的属性查找并执行一些操作以确保其不会失败:
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__
会拦截 EVERY 属性查找,无论该属性是否存在无关紧要。
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属性;它已经变得“无法到达”。