__getattr__和__getattribute__的区别

我试图理解何时定义__getattr____getattribute__python文档提到__getattribute__适用于新样式的类。什么是新型类?

196178 次浏览

新型类是直接或间接继承“object”的类。除了__init__之外,它们还有一个__new__类方法,并且有一些更合理的低级行为。

通常,您会希望覆盖__getattr__(如果您正在覆盖其中任何一个),否则您将很难支持“self”。方法中的Foo语法。

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

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

class SomeObject(object):
pass


class SubObject(SomeObject):
pass

老式的类没有:

class SomeObject:
pass

这只适用于python2 -在python3中,上述所有将创建新样式的类。

详见9. 类< / > (Python教程),NewClassVsClassicClass Python中新旧样式类的区别是什么?< / >

__getattr____getattribute__之间的一个关键区别是,__getattr__只在没有以通常的方式找到属性时才被调用。它很适合为缺失的属性实现回退,并且可能是您想要的两种方法之一。

__getattribute__是在查看对象上的实际属性之前调用的,因此正确实现可能比较棘手。你可以很容易地得到无限递归。

新风格的类来自object,旧风格的类来自Python 2。没有显式基类的X。但是,在__getattr____getattribute__之间进行选择时,新旧类型之间的区别并不重要。

您几乎肯定想要__getattr__

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

# EYZ0例子:

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

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

# EYZ0

每当你请求一个尚未定义的属性时,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)

# EYZ0

现在让我们看看__getattribute__方法。如果你的类中有__getattribute__方法,python会为每个属性调用这个方法,不管它是否存在。为什么我们需要__getattribute__方法?一个很好的理由是,您可以阻止对属性的访问,并使它们更加安全,如下面的示例所示。

每当有人试图访问以子字符串“坏蛋”开头的属性时,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)

在阅读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是Object A的属性名称,该属性是Object B。当Object B上定义的方法在Object A上被调用时,__getattr__方法将在查找链的末尾被调用。这也会使代码更干净,因为您不需要为委托给另一个对象而定义一个方法列表。

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

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

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

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

< p > . .c:function: PyObject* PyObject_GenericGetAttr(PyObject *o, 一般的属性getter函数 放在类型对象的tp_getattro插槽中。它寻找一个 对象的MRO中的类字典中的描述符 作为对象的:attr:~对象中的属性。# EYZ0(如果 礼物)。如:ref:descriptors中所述,数据描述符取 优先于实例属性,而非数据描述符 不喜欢。否则,将引发:exc:AttributeError异常

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

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

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

这有一个明确的含义:因为__getattribute__有默认实现,而__getattr__没有,显然python鼓励用户实现__getattr__