我想了解何时使用__getattr__
或__getattribute__
。 documentation提到__getattribute__
适用于新式课程。什么是新式课程?
答案 0 :(得分:405)
__getattr__
和__getattribute__
之间的主要区别在于,只有在找不到通常方式的属性时,才会调用__getattr__
。这对于实现缺失属性的后备是有好处的,并且可能是您想要的两个中的一个。
__getattribute__
,因此正确实现可能很棘手。你可以很容易地进行无限递归。
新式类派生自object
,旧式类是Python 2.x中没有明确基类的类。但是,在__getattr__
和__getattribute__
之间进行选择时,旧式和新式课程之间的区别并不重要。
你几乎肯定想要__getattr__
。
答案 1 :(得分:95)
让我们看一下__getattr__
和__getattribute__
魔术方法的一些简单示例。
__getattr__
每当您请求尚未定义的属性时,Python都会调用__getattr__
方法。在以下示例中,我的类计数没有__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'
现在我的班级计数有__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)
答案 2 :(得分:15)
新式类继承自object
或其他新样式类:
class SomeObject(object):
pass
class SubObject(SomeObject):
pass
旧式课不:
class SomeObject:
pass
这仅适用于Python 2 - 在Python 3中,以上所有内容都将创建新式类。
有关详细信息,请参阅 9. Classes (Python教程),NewClassVsClassicClass和 What is the difference between old style and new style classes in Python? 。
答案 3 :(得分:14)
这只是基于Ned Batchelder's explanation的示例。
__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 :(得分:6)
新式类是“对象”(直接或间接)的子类。除了__new__
之外,他们还有__init__
类方法,并且有一些更合理的低级行为。
通常,你会想要覆盖__getattr__
(如果你要覆盖它们),否则你将很难在你的方法中支持“self.foo”语法。
答案 5 :(得分:2)
在阅读Beazley&Jones PCB时,我偶然发现了tinycolor.js
的一个明确而实际的用例,可以帮助回答OP的“何时”部分。从书中:
“ __getattr__
方法有点像属性查找的全部。如果代码尝试访问不存在的属性,则该方法会被调用。”我们从以上答案中知道了这一点,但是在PCB配方8.15中,此功能用于实现委派设计模式。如果对象A具有对象B的属性,该属性实现了对象A要委派的许多方法,而不是在对象A中重新定义对象B的所有方法只是为了调用对象B的方法,请按以下方式定义__getattr__()
方法:< / p>
__getattr__()
其中_b是对象A的属性名称,即对象B。在对象A上调用在对象B上定义的方法时,def __getattr__(self, name):
return getattr(self._b, name)
方法将在查找链的末尾被调用。这也将使代码更简洁,因为您没有定义仅用于委派给另一个对象的方法列表。
答案 6 :(得分:2)
返回 __ getattribute __ 功能;如果默认实现未被覆盖;执行该方法时会进行以下检查:
这是对象的实际实现。__getattribute__方法:
.. c:function :: PyObject * PyObject_GenericGetAttr(PyObject * o, PyObject * name)通用属性获取器函数,旨在 放入类型对象的tp_getattro插槽中。它寻找一个 对象的MRO中的类字典中的描述符 作为对象的:attr:〜object中的属性。 dict (如果 当下)。如:ref:descriptors中所述,数据描述符采用 优先于实例属性,而非数据描述符 别。否则,将引发:exc:AttributeError。
答案 7 :(得分:0)
我发现没有人提到这一区别:
__getattribute__
具有默认实现,但__getattr__
没有默认实现。
class A:
pass
a = A()
a.__getattr__ # error
a.__getattribute__ # return a method-wrapper
这具有明确的含义:由于__getattribute__
具有默认实现,而__getattr__
没有默认实现,因此显然python鼓励用户实现__getattr__
。