理解__getattr__和__getattribute__之间的区别

时间:2010-11-28 06:23:07

标签: python oop encapsulation getattr getattribute

我想了解__getattr____getattribute__之间的区别,但是,我没有做到这一点。

Stack Overflow问题的answer Difference between __getattr__ vs __getattribute__ 说:

  在查看实际属性之前调用

__getattribute__   对象,所以可能很棘手   正确实施。你最终可以进入   无限递归非常容易。

我完全不知道这意味着什么。

然后继续说:

  

你几乎肯定想要__getattr__

为什么呢?

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

我正在寻找一些代码示例来清除这个问题。我用Google搜索了我的能力,但我找到的答案并没有彻底讨论这个问题。

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

4 个答案:

答案 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}}属性。这会调用ff.__getattribute__('a')然后尝试加载__getattribute__self.__dict____dict__的属性,因此python调用self == f再次尝试访问属性f.__getattribute__('__dict__')'。这是无限递归。

如果使用了'__dict__,那么

  1. 它永远不会运行,因为__getattr__具有f属性。
  2. 如果它已经运行,(假设你要求a)那么它就不会被调用来查找f.b,因为它已经在那里,只有当<{1}}被调用时才会被调用em>查找属性的所有其他方法都失败。
  3. 使用__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属性;它已经变得“无法到达”。