506

我试图了解何时使用__getattr__or __getattribute__文档中提到的__getattribute__适用于新式类。什么是新式班?

4

8 回答 8

591

__getattr__和之间的一个关键区别__getattribute____getattr__只有在没有以通常方式找到属性时才会调用它。它有利于实现缺失属性的后备,并且可能是您想要的两个之一。

__getattribute__在查看对象的实际属性之前调用,因此正确实现可能很棘手。您可以很容易地以无限递归结束。

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

你几乎肯定想要__getattr__

于 2010-07-19T02:22:00.353 回答
198

让我们看一些简单的例子__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'

现在我的班级Count__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)

__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)

重要提示:为了避免__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)
于 2016-08-05T11:37:09.870 回答
18

这只是一个基于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')

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

于 2013-09-16T06:11:50.943 回答
15

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

class SomeObject(object):
    pass

class SubObject(SomeObject):
    pass

旧式课程不会:

class SomeObject:
    pass

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

请参阅9. 类(Python 教程)、NewClassVsClassicClass和Python中旧样式和新样式类的区别是什么?详情。

于 2010-07-19T02:18:53.823 回答
6

新式类是“对象”的子类(直接或间接)。除了具有__new__类方法之外,它们还__init__具有更合理的低级行为。

通常,您会想要覆盖__getattr__(如果您要覆盖其中任何一个),否则您将很难在方法中支持“self.foo”语法。

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

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

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

  • 检查MRO链中任意一个类中是否定义了同名(属性名)的描述符(方法对象解析)
  • 然后查看实例的命名空间
  • 然后查看类命名空间
  • 然后进入每个基地的命名空间等等。
  • 最后,如果没有找到,默认实现调用实例的后备getattr () 方法,并引发 AttributeError 异常作为默认实现。

这是object.__getattribute__ 方法的实际实现:

.. c:function:: PyObject* PyObject_GenericGetAttr(PyObject *o, PyObject *name) 用于放入类型对象的 tp_getattro 槽的通用属性 getter 函数。它在对象的 MRO 中的类字典中查找描述符以及对象的 :attr:~object 中的属性。字典(如果存在)。如 :ref:descriptors 中所述,数据描述符优先于实例属性,而非数据描述符则不然。否则,会引发 :exc:AttributeError 。

于 2020-06-21T08:28:40.377 回答
3

在阅读 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)

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

于 2020-04-13T13:59:25.673 回答
3

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

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

class A:
    pass
a = A()
a.__getattr__ # error
a.__getattribute__ # return a method-wrapper

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

于 2020-08-13T18:14:47.130 回答