__getattr__ 和 __getattribute__ 之间的区别
- 2025-01-03 08:40:00
- admin 原创
- 102
问题描述:
我想了解何时定义__getattr__
或__getattribute__
。python文档中提到的__getattribute__
适用于新式类。什么是新式类?
解决方案 1:
__getattr__
和之间的主要区别__getattribute__
在于,__getattr__
只有当属性无法以常规方式找到时才会调用。它非常适合实现缺失属性的后备,并且可能是您想要的两个之一。
__getattribute__
在查看对象的实际属性之前被调用,因此很难正确实现。你很容易陷入无限递归。
新式类派生自object
,旧式类是 Python 2.x 中没有明确基类的类。但在__getattr__
和之间进行选择时,旧式类和新式类之间的区别并不是最重要的__getattribute__
。
您几乎肯定想要__getattr__
。
解决方案 2:
让我们看一些有关魔术方法的简单__getattr__
例子__getattribute__
。
__getattr__
__getattr__
每当您请求尚未定义的属性时,Python 都会调用 方法。在下面的例子中,我的类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]
重要的
如果您的类同时包含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)
解决方案 3:
这只是基于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
解决方案 4:
新式类继承自object
,或继承自另一个新式类:
class SomeObject(object):
pass
class SubObject(SomeObject):
pass
旧式课程没有:
class SomeObject:
pass
这仅适用于 Python 2 - 在 Python 3 中,以上所有内容都将创建新式类。
有关详细信息,请参阅9. 类(Python 教程)、NewClassVsClassicClass和Python 中旧式类和新式类之间有什么区别?。
解决方案 5:
新式类是直接或间接地将“对象”子类化的类。它们__new__
除了类方法之外__init__
,还具有更合理的低级行为。
通常,您会想要覆盖__getattr__
(如果您要覆盖的话),否则您将很难在您的方法中支持“self.foo”语法。
额外信息: http: //www.devx.com/opensource/Article/31482/0/page/4
解决方案 6:
我发现没有人提到这个区别:
__getattribute__
有默认实现,但是__getattr__
没有。
class A:
pass
a = A()
a.__getattr__ # error
a.__getattribute__ # return a method-wrapper
这含义很明确:因为__getattribute__
有默认实现,而__getattr__
没有,所以显然python鼓励用户去实现__getattr__
。
解决方案 7:
getattribute:用于从实例中检索属性。它捕获使用点符号或 getattr() 内置函数访问实例属性的每次尝试。
getattr:当对象中未找到属性时,作为最后一个资源执行。您可以选择返回默认值或引发 AttributeError。
回到getattribute函数;如果默认实现没有被覆盖;执行该方法时将进行以下检查:
检查MRO链中任意一个类中是否有定义同名(属性名)的描述符(方法对象解析)
然后查看实例的命名空间
然后查看类命名空间
然后进入每个基地的命名空间等等。
最后,如果未找到,默认实现将调用实例的后备getattr () 方法,并引发 AttributeError 异常作为默认实现。
这是object.__getattribute__ 方法的实际实现:
.. c:function:: PyObject PyObject_GenericGetAttr(PyObject o, PyObject *name) 通用属性获取函数,用于放入类型对象的 tp_getattro 槽中。它在对象的 MRO 中的类字典中查找描述符,并在对象的 :attr:~object. dict中查找属性(如果存在)。如 :ref:descriptors 中所述,数据描述符优先于实例属性,而非数据描述符则不然。否则,将引发 :exc:AttributeError。
解决方案 8:
在阅读 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 是对象 A 的属性的名称,该属性是对象 B。当在对象 A 上调用在对象 B 上定义的方法时,__getattr__
将在查找链的末尾调用该方法。这也会使代码更简洁,因为您没有定义仅用于委托给另一个对象的方法列表。