__getattr__与__getattribute__之间的区别

Yar*_*rin 378 python getattr getattribute

我想知道何时使用__getattr____getattribute__.该文件提到了__getattribute__适用于新样式类.什么是新式课程?

Ned*_*der 455

__getattr__和之间的关键区别仅__getattribute__在于__getattr__找不到通常方式的属性时才会调用.这对于实现缺少属性的后备是有好处的,并且可能是您想要的两个中的一个.

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

新式类派生自object,旧式类是Python 2.x中没有明确基类的类.但旧式和新式的类之间的区别并不之间进行选择时的重要的__getattr____getattribute__.

你几乎肯定想要__getattr__.

  • "为了避免在这个方法中无限递归,它的实现应该总是调用具有相同名称的基类方法来访问它需要的任何属性,例如,object .__ getattribute __(self,name)." (23认同)
  • @Alcott:你可以同时实现它们,但我不确定你为什么这么做.将为每次访问调用`__getattribute__`,并且`__getattr ___'将在`__getattribute__`引发`AttributeError`的时间被调用.为什么不把它们全部合二为一? (12认同)
  • @NedBatchelder,如果你想(有条件地)覆盖对现有方法的调用,你会想要使用`__getattribute__`. (7认同)
  • 请注意:要访问“__getattribute__”中的变量而不调用无限递归,必须调用“super().__getattribute__(item)” (5认同)
  • 我可以在同一个类中实现它们吗?如果可以的话,实施这两者有什么用? (4认同)
  • @kmonsoor。这是同时实施这两项措施的一个很好的理由。`objec.__getattribute__` 在适当的情况下调用 `myclass.__getattr__`。 (2认同)

N R*_*awa 114

让我们看一下两个简单的例子__getattr____getattribute__魔术方法.

__getattr__

__getattr__每当您请求尚未定义的属性时,Python都会调用 方法.在以下示例中,我的类Count没有__getattr__方法.现在,在主,当我试图同时访问obj1.myminobj1.mymax属性,一切工作正常.但是当我尝试访问obj1.mycurrent属性时 - Python给了我AttributeError: 'Count' object has no attribute 'mycurrent'

class Count():
    def __init__(self,mymin,mymax):
        self.mymin=mymin
        self.mymax=mymax

obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.mycurrent)  --> AttributeError: 'Count' object has no attribute 'mycurrent'
Run Code Online (Sandbox Code Playgroud)

现在我的类计数__getattr__方法.现在当我尝试访问 obj1.mycurrent属性时 - python返回我在我的__getattr__方法中实现的任何内容.在我的例子中,每当我尝试调用一个不存在的属性时,python就会创建该属性并将其设置为整数值0.

class Count:
    def __init__(self,mymin,mymax):
        self.mymin=mymin
        self.mymax=mymax    

    def __getattr__(self, item):
        self.__dict__[item]=0
        return 0

obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.mycurrent1)
Run Code Online (Sandbox Code Playgroud)

__getattribute__

现在让我们看看__getattribute__方法.如果你__getattribute__的类中有 方法,python会为每个属性调用此方法,无论它是否存在.那么为什么我们需要__getattribute__方法?一个很好的理由是您可以阻止对属性的访问并使其更安全,如以下示例所示.

每当有人试图访问以子串'cur'开头的属性时,python会引发AttributeError异常.否则返回该属性.

class Count:

    def __init__(self,mymin,mymax):
        self.mymin=mymin
        self.mymax=mymax
        self.current=None

    def __getattribute__(self, item):
        if item.startswith('cur'):
            raise AttributeError
        return object.__getattribute__(self,item) 
        # or you can use ---return super().__getattribute__(item)

obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.current)
Run Code Online (Sandbox Code Playgroud)

要点:为了避免__getattribute__方法中的无限递归,它的实现应该总是调用具有相同名称的基类方法来访问它需要的任何属性.例如:object.__getattribute__(self, name)或者 super().__getattribute__(item)不是self.__dict__[item]

重要

如果您的类包含getattrgetattribute魔术方法,则 __getattribute__首先调用.但是如果 __getattribute__引发 AttributeError异常,那么异常将被忽略并且__getattr__将调用方法.请参阅以下示例:

class Count(object):

    def __init__(self,mymin,mymax):
        self.mymin=mymin
        self.mymax=mymax
        self.current=None

    def __getattr__(self, item):
            self.__dict__[item]=0
            return 0

    def __getattribute__(self, item):
        if item.startswith('cur'):
            raise AttributeError
        return object.__getattribute__(self,item)
        # or you can use ---return super().__getattribute__(item)
        # note this class subclass object

obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.current)
Run Code Online (Sandbox Code Playgroud)

  • @Rohit `current` 是在 `Count` 实例上定义的(请参阅 `__init__`),因此如果属性不存在,则简单地引发 `AttributeError` 并不完全是发生的事情 - 它遵循 `__getattr__` 对于 _all_ 名称开始“cur”,包括“current”,还包括“curious”、“curly”... (3认同)

Sam*_*lan 15

新样式类继承object自另一个新样式类:

class SomeObject(object):
    pass

class SubObject(SomeObject):
    pass
Run Code Online (Sandbox Code Playgroud)

旧式课不:

class SomeObject:
    pass
Run Code Online (Sandbox Code Playgroud)

这仅适用于Python 2 - 在Python 3中,以上所有内容都将创建新式类.

请参阅9.类(Python教程),NewClassVsClassicClass以及Python中旧样式和新样式类之间的区别是什么?详情.


Sim*_*4ya 14

这只是基于Ned Batchelder的解释的一个例子.

__getattr__ 例:

class Foo(object):
    def __getattr__(self, attr):
        print "looking up", attr
        value = 42
        self.__dict__[attr] = value
        return value

f = Foo()
print f.x 
#output >>> looking up x 42

f.x = 3
print f.x 
#output >>> 3

print ('__getattr__ sets a default value if undefeined OR __getattr__ to define how to handle attributes that are not found')
Run Code Online (Sandbox Code Playgroud)

如果使用相同的示例,__getattribute__您将获得>>>RuntimeError: maximum recursion depth exceeded while calling a Python object

  • **其实,这是可怕的.**真实世界'__getattr __()`实现只有通过提高`AttributeError`无效的属性名,从而避免[微妙和难以调试的问题]接受有限的一组有效的属性名称(https://code.activestate.com/recipes/52238-__getattr__-handle-with-care).此示例无条件接受_all_属性名称为有效 - 的`__getattr __()`一个奇异(坦白地说容易出错)误用.如果您希望在此示例中对属性创建进行"完全控制",则需要使用`__getattribute __()`. (8认同)
  • @CecilCurry:你链接到的所有问题都涉及隐式返回None而不是一个值,这个答案没有.接受所有属性名称有什么问题?它与`defaultdict`相同. (2认同)
  • 问题是 `__getattr__` 将在 *before* 超类查找之前被调用。这对于 `object` 的直接子类来说是可以的,因为你真正关心的唯一方法是无论如何都会忽略实例的魔法方法,但是对于任何更复杂的继承结构,你完全删除了从父类继承任何东西的能力。 (2认同)
  • @Simon K Bhatta4ya,你最后的打印语句是一条评论。正确的?这是一长行,读起来很乏味(必须在右侧滚动很多)。将这一行放在代码部分之后怎么样?或者如果你想把它放在代码部分,我认为最好将它分成两行。 (2认同)

Mr *_*ooz 6

新式类是"对象"(直接或间接)的子类.它们除了具有__new__类方法,__init__并且具有更合理的低级行为.

通常,你会想要覆盖__getattr__(如果你要覆盖它们),否则你将很难在你的方法中支持"self.foo"语法.

额外信息:http://www.devx.com/opensource/Article/31482/0/page/4


小智 6

    \n
  • getattribute:用于从实例检索属性。它捕获使用点表示法或 getattr() 内置函数访问实例属性的每次尝试。
  • \n
  • getattr:当在对象中找不到属性时,作为最后一个资源执行。您可以选择返回默认值或引发 AttributeError。
  • \n
\n

回到__getattribute__函数;如果默认实现未被覆盖;执行该方法时会进行以下检查:

\n
    \n
  • 检查MRO链中任意类中是否存在定义同名(属性名)的描述符(方法对象解析)
  • \n
  • 然后查看instance\xe2\x80\x99s命名空间
  • \n
  • 然后查看类名称空间
  • \n
  • 然后进入每个base\xe2\x80\x99s命名空间等等。
  • \n
  • 最后,如果没有找到,默认实现调用后备getattr() method of the instance and it raises an AttributeError exception as default implementation.
  • \n
\n

这是实际的实现 of the object.__getattribute__ method:

\n
\n

.. c:function:: PyObject* PyObject_GenericGetAttr(PyObject *o,\nPyObject *name) 通用属性 getter 函数,用于\n放入类型对象的 tp_getattro 槽中。它在对象的 MRO 中的类字典中查找描述符,并在对象的 :attr:~object 中查找属性。词典 (if\npresent). As outlined in :ref:descriptors, data descriptors take\npreference over instance attributes, while non-data descriptors\ndon't. Otherwise, an :exc:AttributeError is raised.

\n
\n


you*_*hao 6

我发现没有人提到这个区别:

__getattribute__有一个默认实现,但__getattr__没有。

class A:
    pass
a = A()
a.__getattr__ # error
a.__getattribute__ # return a method-wrapper
Run Code Online (Sandbox Code Playgroud)

这有一个明确的含义:既然__getattribute__有默认实现,而__getattr__没有,显然 python 鼓励用户实现__getattr__.


sop*_*312 5

在阅读 Beazley & Jones PCB 时,我偶然发现了一个明确且实用的用例,它__getattr__有助于回答 OP 问题的“何时”部分。摘自书中:

“该__getattr__()方法有点像属性查找的包罗万象。如果代码尝试访问不存在的属性,就会调用该方法。” 我们从上面的答案中知道了这一点,但在 PCB 配方 8.15 中,此功能用于实现委托设计模式。如果对象A有一个属性对象B实现了对象A想要委托的很多方法,那么与其在对象A中重新定义对象B的所有方法只是为了调用对象B的方法,不如定义一个方法如下__getattr__()

def __getattr__(self, name):
    return getattr(self._b, name)
Run Code Online (Sandbox Code Playgroud)

其中 _b 是对象 A 的属性名称,该属性是对象 B。当在对象 A 上调用对象 B 上定义的方法时,__getattr__将在查找链的末尾调用该方法。这也会使代码更清晰,因为您没有仅为委托给另一个对象而定义的方法列表。