__getattr__和__getattribute__之间的区别

问题:__getattr__和__getattribute__之间的区别

我想了解何时使用__getattr____getattribute__。该文件提到了__getattribute__适用于新样式类。什么是新型类?

I am trying to understand when to use __getattr__ or __getattribute__. The documentation mentions __getattribute__ applies to new-style classes. What are new-style classes?


回答 0

之间的主要差异__getattr__,并__getattribute__是,__getattr__如果属性没有被发现通常的途径,只调用。这对于实现缺少属性的后备方法很有用,并且可能是您想要的两个之一。

__getattribute__在查看对象的实际属性之前调用,因此正确实现可能会比较棘手。您可以非常轻松地进行无限递归。

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

您几乎可以肯定想要__getattr__

A key difference between __getattr__ and __getattribute__ is that __getattr__ is only invoked if the attribute wasn’t found the usual ways. It’s good for implementing a fallback for missing attributes, and is probably the one of two you want.

__getattribute__ is invoked before looking at the actual attributes on the object, and so can be tricky to implement correctly. You can end up in infinite recursions very easily.

New-style classes derive from object, old-style classes are those in Python 2.x with no explicit base class. But the distinction between old-style and new-style classes is not the important one when choosing between __getattr__ and __getattribute__.

You almost certainly want __getattr__.


回答 1

让我们来看看两者的一些简单的例子__getattr____getattribute__魔术方法。

__getattr__

__getattr__每当您请求尚未定义的属性时,Python都会调用 method。在下面的示例中,我的Count类没有__getattr__方法。现在,当我尝试同时访问obj1.mymin和访问obj1.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'

现在,我的ClassCount具有__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)

Lets see some simple examples of both __getattr__ and __getattribute__ magic methods.

__getattr__

Python will call __getattr__ method whenever you request an attribute that hasn’t already been defined. In the following example my class Count has no __getattr__ method. Now in main when I try to access both obj1.mymin and obj1.mymax attributes everything works fine. But when I try to access obj1.mycurrent attribute — Python gives me 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'

Now my class Count has __getattr__ method. Now when I try to access obj1.mycurrent attribute — python returns me whatever I have implemented in my __getattr__ method. In my example whenever I try to call an attribute which doesn’t exist, python creates that attribute and set it to integer value 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__

Now lets see the __getattribute__ method. If you have __getattribute__ method in your class, python invokes this method for every attribute regardless whether it exists or not. So why we need __getattribute__ method? One good reason is that you can prevent access to attributes and make them more secure as shown in the following example.

Whenever someone try to access my attributes that starts with substring ‘cur’ python raises AttributeError exception. Otherwise it returns that attribute.

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)

Important: In order to avoid infinite recursion in __getattribute__ method, its implementation should always call the base class method with the same name to access any attributes it needs. For example: object.__getattribute__(self, name) or super().__getattribute__(item) and not self.__dict__[item]

IMPORTANT

If your class contain both getattr and getattribute magic methods then __getattribute__ is called first. But if __getattribute__ raises AttributeError exception then the exception will be ignored and __getattr__ method will be invoked. See the following example:

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

这只是基于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

This is just an example based on Ned Batchelder’s explanation.

__getattr__ example:

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

And if same example is used with __getattribute__ You would get >>> RuntimeError: maximum recursion depth exceeded while calling a Python object


回答 3

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

class SomeObject(object):
    pass

class SubObject(SomeObject):
    pass

旧式类不能:

class SomeObject:
    pass

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

请参见9.类(Python教程),NewClassVsClassicClass和Python中旧样式类和新样式类之间的区别是什么?有关详细信息。

New-style classes inherit from object, or from another new style class:

class SomeObject(object):
    pass

class SubObject(SomeObject):
    pass

Old-style classes don’t:

class SomeObject:
    pass

This only applies to Python 2 – in Python 3 all the above will create new-style classes.

See 9. Classes (Python tutorial), NewClassVsClassicClass and What is the difference between old style and new style classes in Python? for details.


回答 4

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

通常,您将需要覆盖__getattr__(如果要覆盖两者之一),否则您将很难在方法中支持“ self.foo”语法。

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

New-style classes are ones that subclass “object” (directly or indirectly). They have a __new__ class method in addition to __init__ and have somewhat more rational low-level behavior.

Usually, you’ll want to override __getattr__ (if you’re overriding either), otherwise you’ll have a hard time supporting “self.foo” syntax within your methods.

Extra info: http://www.devx.com/opensource/Article/31482/0/page/4


回答 5

在阅读Beazley&Jones PCB时,我偶然发现了一个明确而实际的用例,__getattr__该用例有助于回答OP的“何时”部分。从书中:

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

def __getattr__(self, name):
    return getattr(self._b, name)

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

In reading through Beazley & Jones PCB, I have stumbled on an explicit and practical use-case for __getattr__ that helps answer the “when” part of the OP’s question. From the book:

“The __getattr__() method is kind of like a catch-all for attribute lookup. It’s a method that gets called if code tries to access an attribute that doesn’t exist.” We know this from the above answers, but in PCB recipe 8.15, this functionality is used to implement the delegation design pattern. If Object A has an attribute Object B that implements many methods that Object A wants to delegate to, rather than redefining all of Object B’s methods in Object A just to call Object B’s methods, define a __getattr__() method as follows:

def __getattr__(self, name):
    return getattr(self._b, name)

where _b is the name of Object A’s attribute that is an Object B. When a method defined on Object B is called on Object A, the __getattr__ method will be invoked at the end of the lookup chain. This would make code cleaner as well, since you do not have a list of methods defined just for delegating to another object.