__getattr__和__getattribute__之间的区别


Answers:


497

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

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

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

您几乎可以肯定想要__getattr__


6
我可以在同一个类中实现它们吗?如果可以的话,两者的实现又是什么?
奥尔科特

13
@Alcott:您可以同时实现它们,但我不确定为什么会实现。 __getattribute__每次访问__getattr__都会被调用,并且会在__getattribute__引发的时间被调用AttributeError。为什么不将其全部合而为一呢?
Ned Batchelder

10
@NedBatchelder,如果要(有条件地)覆盖对现有方法的调用,则需要使用__getattribute__
杰斯·布朗宁

28
“为了避免此方法的无限递归,其实现应始终调用具有相同名称的基类方法以访问其所需的任何属性,例如object .__ getattribute __(self,name)。”
kmonsoor 2014年

1
@kmonsoor。这是同时实现这两个方面的一个很好的理由。在适当的情况下objec.__getattribute__调用myclass.__getattr__
疯狂物理学家

138

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

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

1
我不确定要重写的确切用例是什么,__getattribute__但是肯定不是。因为按照您的示例,如果对象中没有该属性,则您正在做的所有事情__getattribute__都会引发AttributeError异常__dict__。但您实际上并不需要它,因为这是的默认实现,__getattribute____getattr__实际上正是您作为后备机制所需要的。
罗希特(Rohit)'18

@Rohit current定义上的实例Count(见__init__),所以仅仅是提高AttributeError如果属性是不是有一种不太发生的事情-它推迟到__getattr__所有的名字开始“小人”,包括current,也curiouscurly...
joelb

16

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


9
实际上,这太可怕了。现实世界中的__getattr__()实现只通过增加AttributeError无效的属性名称来接受一组有限的有效属性名称,从而避免了细微而难以调试的问题。此示例无条件地接受所有属性名称为有效–奇怪的(坦率地说,容易出错)滥用__getattr__()。如本例所示,如果要“完全控制”属性创建,则需要__getattribute__()
塞西尔·库里

3
@CecilCurry:您链接到的所有问题都涉及隐式返回None而不是返回值,而此答案不会这样做。接受所有属性名称有什么问题?与相同defaultdict
尼克·马特奥

2
问题是__getattr__超类查找之前调用它。对于的直接子类来说,这是可以的object,因为您真正关心的唯一方法是总会忽略该实例的魔术方法,但是对于任何更复杂的继承结构,您都完全无法从父级继承任何东西。
疯狂物理学家

1
@Simon K Bhatta4ya,您的上次印刷声明为评论。对?这是一条长长的线,阅读起来很繁琐(必须在右侧滚动很多次)。将该行放在代码段后面怎么办?或者,如果您想将其放入代码部分,我认为将其分为两行会更好。
阿布·纳菲伊卜·伊卜娜·扎希德医师


6

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

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

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


2

在阅读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__方法时,该方法将在查找链的末尾被调用。这也将使代码更简洁,因为您没有定义仅用于委派给另一个对象的方法列表。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.