我想了解何时使用__getattr__或__getattribute__。该文件提到了__getattribute__适用于新样式类。什么是新型类?
我想了解何时使用__getattr__或__getattribute__。该文件提到了__getattribute__适用于新样式类。什么是新型类?
Answers:
之间的主要差异__getattr__,并__getattribute__是,__getattr__如果属性没有被发现通常的途径,只调用。这对于实现缺少属性的后备方法很有用,并且可能是您想要的两个之一。
__getattribute__在查看对象的实际属性之前调用,因此正确实现可能会比较棘手。您可以非常轻松地进行无限递归。
新样式类是从派生而来的object,旧样式类是Python 2.x中没有显式基类的类。但旧式和新式的类之间的区别并不之间进行选择时的重要的__getattr__和__getattribute__。
您几乎可以肯定想要__getattr__。
__getattribute__每次访问__getattr__都会被调用,并且会在__getattribute__引发的时间被调用AttributeError。为什么不将其全部合而为一呢?
__getattribute__。
objec.__getattribute__调用myclass.__getattr__。
让我们来看看两者的一些简单的例子__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]
如果您的类同时包含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)
__getattribute__但是肯定不是。因为按照您的示例,如果对象中没有该属性,则您正在做的所有事情__getattribute__都会引发AttributeError异常__dict__。但您实际上并不需要它,因为这是的默认实现,__getattribute__而__getattr__实际上正是您作为后备机制所需要的。
current定义上的实例Count(见__init__),所以仅仅是提高AttributeError如果属性是不是有一种不太发生的事情-它推迟到__getattr__了所有的名字开始“小人”,包括current,也curious,curly...
这只是基于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
__getattr__()实现只通过增加AttributeError无效的属性名称来接受一组有限的有效属性名称,从而避免了细微而难以调试的问题。此示例无条件地接受所有属性名称为有效–奇怪的(坦率地说,容易出错)滥用__getattr__()。如本例所示,如果要“完全控制”属性创建,则需要__getattribute__()。
defaultdict。
__getattr__将在超类查找之前调用它。对于的直接子类来说,这是可以的object,因为您真正关心的唯一方法是总会忽略该实例的魔术方法,但是对于任何更复杂的继承结构,您都完全无法从父级继承任何东西。
新样式类继承自object,或从另一个新样式类继承:
class SomeObject(object):
pass
class SubObject(SomeObject):
pass
旧式类不能:
class SomeObject:
pass
这仅适用于Python 2-在Python 3中,以上所有内容都会创建新样式的类。
请参见9.类(Python教程),NewClassVsClassicClass和Python中旧样式类和新样式类之间的区别是什么?有关详细信息。
新型类是子类“对象”的子类(直接或间接)。他们除了具有__new__类方法外__init__,还具有更合理的低级行为。
通常,您将需要覆盖__getattr__(如果要覆盖两者之一),否则您将很难在方法中支持“ self.foo”语法。
额外信息:http : //www.devx.com/opensource/Article/31482/0/page/4
在阅读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__方法时,该方法将在查找链的末尾被调用。这也将使代码更简洁,因为您没有定义仅用于委派给另一个对象的方法列表。