__getattr__与__getattribute__之间的区别

时间:2010-07-19 02:11:19

标签: python getattr getattribute

我想了解何时使用__getattr____getattribute__documentation提到__getattribute__适用于新式课程。什么是新式课程?

8 个答案:

答案 0 :(得分:405)

__getattr____getattribute__之间的主要区别在于,只有在找不到通常方式的属性时,才会调用__getattr__。这对于实现缺失属性的后备是有好处的,并且可能是您想要的两个中的一个。

在查看对象的实际属性之前调用

__getattribute__,因此正确实现可能很棘手。你可以很容易地进行无限递归。

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

你几乎肯定想要__getattr__

答案 1 :(得分:95)

让我们看一下__getattr____getattribute__魔术方法的一些简单示例。

__getattr__

每当您请求尚未定义的属性时,Python都会调用__getattr__方法。在以下示例中,我的类计数没有__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'

现在我的班级计数__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]

重要

如果您的课程同时包含 getattr getattribute 魔术方法,则首先调用__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)

答案 2 :(得分:15)

新式类继承自object或其他新样式类:

class SomeObject(object):
    pass

class SubObject(SomeObject):
    pass

旧式课不:

class SomeObject:
    pass

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

有关详细信息,请参阅 9. Classes (Python教程),NewClassVsClassicClass What is the difference between old style and new style classes in Python?

答案 3 :(得分:14)

这只是基于Ned Batchelder's explanation的示例。

__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

答案 4 :(得分:6)

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

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

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

答案 5 :(得分:2)

在阅读Beazley&Jones PCB时,我偶然发现了tinycolor.js的一个明确而实际的用例,可以帮助回答OP的“何时”部分。从书中:

__getattr__方法有点像属性查找的全部。如果代码尝试访问不存在的属性,则该方法会被调用。”我们从以上答案中知道了这一点,但是在PCB配方8.15中,此功能用于实现委派设计模式。如果对象A具有对象B的属性,该属性实现了对象A要委派的许多方法,而不是在对象A中重新定义对象B的所有方法只是为了调用对象B的方法,请按以下方式定义__getattr__()方法:< / p>

__getattr__()

其中_b是对象A的属性名称,即对象B。在对象A上调用在对象B上定义的方法时,def __getattr__(self, name): return getattr(self._b, name) 方法将在查找链的末尾被调用。这也将使代码更简洁,因为您没有定义仅用于委派给另一个对象的方法列表。

答案 6 :(得分:2)

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

返回 __ getattribute __ 功能;如果默认实现未被覆盖;执行该方法时会进行以下检查:

  • 检查在MRO链中的任何类(方法对象解析)中是否存在具有相同名称(属性名称)的描述符
  • 然后查看实例的名称空间
  • 然后查看类名称空间
  • 然后进入每个基地的名称空间,依此类推。
  • 最后,如果未找到,默认实现将调用实例的后备 getattr ()方法,并会引发AttributeError异常作为默认实现。

这是对象的实际实现。__getattribute__方法:

.. c:function :: PyObject * PyObject_GenericGetAttr(PyObject * o, PyObject * name)通用属性获取器函数,旨在 放入类型对象的tp_getattro插槽中。它寻找一个 对象的MRO中的类字典中的描述符 作为对象的:attr:〜object中的属性。 dict (如果 当下)。如:ref:descriptors中所述,数据描述符采用 优先于实例属性,而非数据描述符 别。否则,将引发:exc:AttributeError。

答案 7 :(得分:0)

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

__getattribute__具有默认实现,但__getattr__没有默认实现。

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

这具有明确的含义:由于__getattribute__具有默认实现,而__getattr__没有默认实现,因此显然python鼓励用户实现__getattr__