关于python:__getattr__与__getattribute__之间的区别

Difference between __getattr__ vs __getattribute__

我试图理解何时使用__getattr____getattribute__。文档中提到的__getattribute__适用于新样式的类。什么是新型课程?


__getattr____getattribute__之间的一个关键区别是,只有在找不到通常的方法时,才会调用__getattr__。它有助于为缺少的属性实现回退,并且可能是您想要的两个属性之一。

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

新样式类派生自object,旧样式类是Python2.x中没有显式基类的类。但是,在选择__getattr____getattribute__时,新旧阶级的区别并不是很重要。

你几乎肯定想要__getattr__


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

埃多克斯1〔2〕

每当您请求一个尚未定义的属性时,python将调用__getattr__方法。在下面的示例中,我的类计数没有__getattr__方法。现在,当我试图访问obj1.myminobj1.mymax属性时,基本上一切都正常。但是当我试图访问obj1.mycurrent属性时——python给了我AttributeError: 'Count' object has no attribute 'mycurrent'

1
2
3
4
5
6
7
8
9
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。

1
2
3
4
5
6
7
8
9
10
11
12
13
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)

埃多克斯1〔5〕

现在我们来看看__getattribute__方法。如果类中有__getattribute__方法,不管每个属性是否存在,python都会为其调用此方法。那么为什么我们需要__getattribute__方法呢?一个很好的原因是,您可以阻止访问属性,并使它们更安全,如下面的示例所示。

每当有人试图访问以子字符串"cur"开头的属性时,python就会引发AttributeError异常。否则它将返回该属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 magic方法,则首先调用__getattribute__。但如果__getattribute__上调AttributeError异常,则忽略该异常并调用__getattr__方法。请参见以下示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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)


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

1
2
3
4
5
class SomeObject(object):
    pass

class SubObject(SomeObject):
    pass

旧样式类不:

1
2
class SomeObject:
    pass

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

见9。类(python教程)、newclassvsclassicclass以及python中的旧样式类和新样式类之间的区别是什么?有关详细信息。


这只是一个基于内德·巴切尔德解释的例子。

__getattr__示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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


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

通常,您需要重写__getattr__(如果您要重写其中一个),否则您将很难在方法中支持"self.foo"语法。

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