`getattr`如何与`object .__ getattribute__`和`object .__ getattr__`相关?

Tim*_*Tim 5 python operators built-in magic-methods python-3.x

来自https://docs.python.org/3.6/library/functions.html#getattr

getattr(object, name[, default])
Run Code Online (Sandbox Code Playgroud)

返回指定属性的值object.name必须是一个字符串.如果字符串是对象属性之一的名称,则结果是该属性的值.例如,getattr(x, 'foobar')相当于x.foobar.如果named属性不存在,default则返回(如果提供),否则 AttributeError引发.

如何getattrobject.__getattribute__object.__getattr__

getattr打电话object.__getattribute__object.__getattr__?我猜这个前者?

Ray*_*ger 6

摘要答案

通常,虚线查找会调用__getattribute__.

如果__getattribute__中的代码没有找到该属性,它会查看是否定义了__getattr__.如果是这样,就会被调用.否则,引发AttributeError.

所述GETATTR()函数仅是调用上述方法的替代方式.例如getattr(a, 'x')相当于a.x.

当您事先不知道属性的名称时(即它存储在变量中),getattr()函数主要有用.例如,k = 'x'; getattr(a, k)相当于a.x.

类比和高水平的观点

想到它的最好方法是__getattribute__是第一个被称为主方法而__getattr__是在缺少属性时调用的回退.这样,它非常像__getitem____missing__之间的关系,用于字典中的方括号查找.

演示代码

这是一个经过深思熟虑的例子:

>>> class A(object):
    x = 10
    def __getattribute__(self, attr):
        print(f'Looking up {attr!r}')
        return object.__getattribute__(self, attr)
    def __getattr__(self, attr):
        print(f'Invoked the fallback method for missing {attr!r}')
        return 42

>>> a = A()
>>> a.x
Looking up 'x'
10
>>> a.y
Looking up 'y'
Invoked the fallback method for missing 'y'
42

>>> # Equivalent calls with getattr()
>>> getattr(a, 'x')
Looking up 'x'
10
>>> getattr(a, 'y')
Looking up 'y'
Invoked the fallback method for missing 'y'
42
Run Code Online (Sandbox Code Playgroud)

官方文件

以下是文档的相关部分:

object .__ getattr __(self,name)当属性查找在通常位置找不到属性时调用(即它不是实例属性,也不在类树中找到self).name是属性名称.此方法应返回(计算)属性值或引发AttributeError异常.

请注意,如果通过常规机制找到该属性,则不会调用__getattr __().(这是__getattr __()和__setattr __()之间的故意不对称.)这是出于效率原因而做的,因为否则__getattr __()将无法访问实例的其他属性.请注意,至少对于实例变量,您可以通过不在实例属性字典中插入任何值来伪造总控制(而是将它们插入另一个对象中).请参阅下面的__getattribute __()方法,以获得实际获得对属性访问的完全控制的方法.

object .__ getattribute __(self,name)无条件调用以实现类实例的属性访问.如果该类还定义了__getattr __(),则除非__getattribute __()显式调用它或引发AttributeError,否则不会调用后者.此方法应返回(计算)属性值或引发AttributeError异常.为了避免此方法中的无限递归,其实现应始终调用具有相同名称的基类方法来访问其所需的任何属性,例如,object .__ getattribute __(self,name).


Ash*_*ary 3

getattr(foo, 'bar')(基本上是foo.bar)调用__getattribute__,并且__getattr__可以在以下情况下调用(如果存在):

  1. AttributeError是由__getattribute__
  2. __getattribute__明确地调用它

来自文档

如果该类还定义了__getattr__(),则不会调用后者,除非__getattribute__()显式调用它或引发 AttributeError.

__getattr__如果您不想在随机属性访问时引发属性错误,则定义会很有用。模拟库就是一个很好的例子。

>>> from unittest.mock import Mock
>>> m = Mock()
>>> m.foo.bar.spam
<Mock name='mock.foo.bar.spam' id='4367274280'>
Run Code Online (Sandbox Code Playgroud)

如果您不想__getattr__一直定义也不想AttributeError一直处理,那么您可以使用 3 个参数形式getattr()在未找到属性时返回默认值,或者使用hasattr来检查其是否存在。

>> getattr([], 'foo')
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-60-f1834c32d3ce> in <module>()
----> 1 getattr([], 'foo')

AttributeError: 'list' object has no attribute 'foo'
>>> getattr([], 'foo', 'default')
'default'
Run Code Online (Sandbox Code Playgroud)

演示:

class A:
    def __getattr__(self, attr):
        print('Inside A.__getattr__')
        return 'eggs'

print(A().foo)
Run Code Online (Sandbox Code Playgroud)

输出:

Inside A.__getattr__
eggs
Run Code Online (Sandbox Code Playgroud)

请注意,数据模型文档将其指出为object.__getattr__,但这并不意味着它们存在于内置object类型上。相反,它们通常可以存在于任何物体上。在这种情况下,对象是一种类型,因为 dunder 方法是在对象的类型上查找的。