理解__getattr__和__getattribute__之间的区别

use*_*312 192 python oop encapsulation getattr getattribute

我试图理解上的差异之间__getattr____getattribute__,但是,我在它失败.

Stack Overflow问题的答案vs 之间的差异__getattr____getattribute__说:

__getattribute__在查看对象的实际属性之前调用,因此正确实现可能很棘手.您最终可以轻松地进行无限递归.

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

然后继续说:

你几乎肯定想要__getattr__.

为什么?

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

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

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

pyf*_*unc 285

首先是一些基础知识.

对于对象,您需要处理其属性.通常我们这样做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']
Run Code Online (Sandbox Code Playgroud)

警告和使用 __getattribute__

如果您需要捕获每个属性,无论它是否存在,请__getattribute__改为使用.不同之处在于__getattr__只会调用实际不存在的属性.如果直接设置属性,则引用该属性将在不调用的情况下检索它__getattr__.

__getattribute__ 被称为所有时间.

  • @Md.AbuNafeeIbnaZahid `getattr` 是一个内置函数。`getattr(foo, 'bar')` 等价于 `foo.bar`。 (3认同)

aar*_*ing 89

__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
Run Code Online (Sandbox Code Playgroud)

这将导致无限递归.这里的罪魁祸首就是这条线return self.__dict__[attr].让我们假装(它足够接近真相)所有属性都存储在self.__dict__其名称中并可由其名称使用.这条线

f.a
Run Code Online (Sandbox Code Playgroud)

试图访问的a属性f.这叫f.__getattribute__('a').__getattribute__然后尝试加载self.__dict__.__dict__self == fpython调用的属性,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)
Run Code Online (Sandbox Code Playgroud)

super(Foo, self).__getattribute__(attr)__getattribute__"最近的"超类的方法(正式地,类的方法解析顺序中的下一个类,或MRO)绑定到当前对象self,然后调用它并让它完成工作.

通过使用__getattr__Python 可以避免所有这些麻烦,直到找不到属性才能使Python 正常运行.那时,Python会控制你的__getattr__方法并让它提出一些东西.

值得注意的是,你可以遇到无限递归__getattr__.

class Foo(object):
    def __getattr__(self, attr):
        return self.attr
Run Code Online (Sandbox Code Playgroud)

我会留下那个作为练习.


Jas*_*ker 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
Run Code Online (Sandbox Code Playgroud)

从交互式翻译:

>>> import prop
>>> class A(prop.PropClass):
...     def f(self):
...             return 1
... 
>>> a = A()
>>> a.f
1
Run Code Online (Sandbox Code Playgroud)

当然这是一个愚蠢的例子,你可能不会想要这样做,但它会向你展示你可以从覆盖中获得的力量__getattribute__.


小智 9

我已经经历了其他人的出色解释。但是,我从这篇博客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'
Run Code Online (Sandbox Code Playgroud)

但如果该属性确实存在,__getattr__则不会被调用:

class Dummy(object):

    def __getattr__(self, attr):
        return attr.upper()

d = Dummy()
d.value = "Python"
print(d.value)  # "Python"
Run Code Online (Sandbox Code Playgroud)

__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?"

Run Code Online (Sandbox Code Playgroud)

在那个例子中,d对象已经有一个属性值。但是当我们尝试访问它时,我们并没有得到最初的期望值(“Python”);我们只是得到任何__getattribute__回报。这意味着我们实际上已经失去了 value 属性;它变得“遥不可及”。