我试图理解__getattr__和__getattribute__之间的区别,然而,我失败了。

堆栈溢出问题__getattr__和__getattribute__之间的差异的答案是:

在查看实际属性之前调用__getattribute__ 对象,等等都是很棘手的 正确实施。你可以在 无限递归非常简单。

我完全不知道那是什么意思。

然后它继续说:

你几乎肯定想要__getattr__。

为什么?

我读到如果__getattribute__失败,__getattr__将被调用。为什么有两种不同的方法做同样的事情呢?如果我的代码实现了新的样式类,我应该使用什么?

我正在寻找一些代码示例来澄清这个问题。我已经尽我所能谷歌了,但我找到的答案并没有彻底讨论这个问题。

如果有任何文件,我准备好阅读了。


当前回答

首先是一些基础知识。

对于对象,您需要处理它们的属性。通常,我们使用instance。attribute。有时我们需要更多的控制(当我们事先不知道属性的名称时)。

例如,instance。属性将变成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__一直被调用。

其他回答

我已经听过了别人的精彩解释。然而,我从这个博客Python Magic Methods和__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__将拦截每个属性查找,不管属性是否存在。

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属性;它已经变得“遥不可及”。

I think the other answers have done a great job of explaining the difference between __getattr__ and __getattribute__, but one thing that might not be clear is why you would want to use __getattribute__. The cool thing about __getattribute__ is that it essentially allows you to overload the dot when accessing a class. This allows you to customize how attributes are accessed at a low level. For instance, suppose I want to define a class where all methods that only take a self argument are treated as properties:

# prop.py
import inspect

class PropClass(object):
    def __getattribute__(self, attr):
        val = super().__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__中获得的强大功能。

每当发生属性访问时调用__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

尝试访问f的a属性。这将调用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().__getattribute__(attr)

super().__getattribute__(attr)将“最近的”超类(正式地说,是类的方法解析顺序(method Resolution Order, MRO)中的下一个类)的__getattribute__方法绑定到当前对象自身,然后调用它并让它完成工作。

所有这些麻烦都可以通过使用__getattr__来避免,它可以让Python做正常的事情,直到找不到属性。在这一点上,Python将控制权交给你的__getattr__方法,并让它想出一些东西。

同样值得注意的是,使用__getattr__可能会遇到无限递归。

class Foo(object):
    def __getattr__(self, attr):
        return self.attr

我把这个留作练习。

首先是一些基础知识。

对于对象,您需要处理它们的属性。通常,我们使用instance。attribute。有时我们需要更多的控制(当我们事先不知道属性的名称时)。

例如,instance。属性将变成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__一直被调用。