【问题标题】:Understanding the difference between __getattr__ and __getattribute__了解 __getattr__ 和 __getattribute__ 之间的区别
【发布时间】:2011-05-16 18:59:26
【问题描述】:

我试图了解__getattr__ 和__getattribute__ 之间的区别,但是我失败了。

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

__getattribute__ 在查看实际属性之前被调用 对象,因此可能很棘手 正确实施。你可以最终在 无限递归非常容易。

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

然后它继续说:

你几乎肯定想要__getattr__。

为什么?

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

我正在寻找一些代码示例来解决这个问题。我已尽我所能在 Google 上搜索,但我找到的答案并没有彻底讨论这个问题。

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

【问题讨论】:

  • 如果有帮助,来自文档“为了避免此方法中的无限递归,其实现应始终调用具有相同名称的基类方法来访问它需要的任何属性,例如,对象.__getattribute__(self, name)。” [docs.python.org/2/reference/…

标签: python oop encapsulation getattr getattribute


【解决方案1】:

先了解一些基础知识。

对于对象,您需要处理它们的属性。通常,我们使用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__ 一直被调用。

【讨论】:

  • " 例如,instance.attribute 将变为getattr(instance, attribute_name)。" .不应该是__getattribute__(instance, attribute_name)吗?
  • @Md.AbuNafeeIbnaZahid getattr 是一个内置函数。 getattr(foo, 'bar') 等价于foo.bar。
  • 值得注意的是,__getattr__ 只有在它被定义时才会被调用,因为它不是从 object 继承的
【解决方案2】:

只要发生属性访问,就会调用__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__,那么

  1. 它永远不会运行,因为f 有一个a 属性。
  2. 如果它已经运行,(假设您要求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__ 方法(正式地,类的方法解析顺序或 MRO 中的下一个类)绑定到当前对象 self,然后调用它并让它执行工作。

使用__getattr__ 可以避免所有这些麻烦,这让Python 做这很正常的事情,直到找不到属性。此时,Python 将控制权交给您的 __getattr__ 方法并让它想出一些东西。

还值得注意的是,__getattr__ 可能会遇到无限递归。

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

我会把那个留作练习。

【讨论】:

  • 当您调用super(Foo, self) 时,您是否明确地获取最近的Foo 父类,然后将实例self 作为第二个参数传入?你能用super()代替super(Foo, self)吗,因为Python 3使用方法解析顺序来隐式找出self最相关的父类(这在使用多重继承时非常有用)??
【解决方案3】:

我认为其他答案很好地解释了__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__ 中获得的力量。

【讨论】:

    【解决方案4】:

    我已经阅读了其他人的出色解释。但是,我从这个博客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__ 返回的任何东西。这意味着我们实际上已经失去了价值属性;它已变得“无法访问”。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-30
      • 2011-10-26
      • 1970-01-01
      • 2020-01-04
      • 2020-03-22
      • 1970-01-01
      相关资源
      最近更新 更多