__getattr__和__getattribute__的区别
我正在尝试理解什么时候应该定义 __getattr__
或 __getattribute__
。Python 的文档提到 __getattribute__
适用于新式类。那么什么是新式类呢?
8 个回答
这只是一个基于 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__
和 __getattribute__
这两个魔法方法。
__getattr__
当你请求一个还没有定义的属性时,Python 会调用 __getattr__
方法。在下面的例子中,我的类 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)
在 __getattr__
和 __getattribute__
之间有一个关键的区别:__getattr__
只有在找不到属性时才会被调用。它非常适合用来处理缺失的属性,可能是你更想用的那个。
__getattribute__
在查看对象的实际属性之前就会被调用,所以实现起来可能会比较复杂。如果不小心,你可能会陷入无限循环。
新式类是从 object
这个类派生出来的,而旧式类是指在 Python 2.x 中没有明确基类的类。不过,在选择 __getattr__
和 __getattribute__
时,旧式类和新式类的区别并不是最重要的。
你几乎肯定会想用 __getattr__
。