__getattr__:__getattr__(self,name)
__getattribute__:__getattribute__(self,name)
其中参数 name 为属性的名称。需要注意的是__getattribute__()仅应用于新式类。
既然这两种方法都用作属性的访问,那么它们有什么区別呢?看一个例子:
class A(object):
def __init__(self,name):
self.name=name
上面的程序输出结果如下:
a=A("attribute")
print(a.name)
attribute
# print(a.test)
def __getattr__(self,name):
print("Calling __getattr__:",name)
再次运行程序会发现输出为:
attribute
('Calling __getattr__', 'test')
None
这次程序没有抛出异常,而是调用了 __getattr__()方法。
实际上__getattr__()方法仅如下情况下才被调用:属件不在实例的__dict__中;
属性不在其基类以及祖先类的__dict__()中;
触发 AttributeError 异常时(注意,不仅仅是__getattribute__()引发的 AttributeError 异常,
property 中定义的 get() 方法抛出异常的时候也会调用该方法)。
需要特別注意的是当这两个方法同时被定义的时候,要么在__getattribute__()中显式调用,
要么触发 AttributeError 异常,否则__getattr__()永远不会被调用。
__getattribute__()及__getattr__()方法都是 Object 类中定义的默认方法,
当用户需要覆盖这些方法时有以下几点注意事项:
1)避免无穷递归。
当在上述例子中添__getattribute__()方法后程序运行会抛出 RimtimeError
异常提示 “ RuntimeError:maximum recursion depth exceeded.”。
def __getattribute__(self,attr):
try:
return self.__dict__[attr]
except KeyError:
return 'default'
这是因为属性的访问调用的是覆盖了的__getattribute__()方法,
而该方法中self.__dict__[attr]又要调用__getattribute__(self,attr),
于是产生了无穷递归,即使将语句self.__dict__[attr]替换为 self.__getattribute__(self,attr)
和 getattr(self,attr)也不能解决问题。正确的 做法是使用 super(obj,sdf).__getattribute__(attr),
因此上面的例子可以改为:super(A,self).__getattribute__(attr)或者 object.__getattribute__(self,attr)。
无穷递归是覆盖__getatt__() 和__getattribute__()方法的时候需要特别小心。
2 )访问未定义的属性。如果在__getattr__()方法中不抛出 AttributeError 异常或者显式返回一个值,
则会返回 None ,此时可能会影响到程序的实际运行预期。看一个示例:
class A(object):
def __init__(self,name):
self.name=name
self.x =20
def __getattr__(self,name):
print("calling __getattr__:",name)
if name == 'z':
return self.x ** 2
elif name =='y':
return self.x ** 3
def __getattribute__(self,attr):
try:
return super(A,self).__getattribute__(attr)
except KeyError:
return 'default'
a=A('attribute')
print(a.name)
attribute
print(a.z)
calling __getattr__: z 400
if hasattr(a,'t'):
c = a.t
print (c)
else:
print("instance a has no attibute t")
calling __getattr__: t calling __getattr__: t None
用户本来的意图是:如果t不属于实例属性,则打印出警告信息,否则给c赋值。
按照用户的理解本来应该是输出警告信息的,可是实际却输出 None 。
这是因为在__getattr__() 方法中没有抛出任何异常也没有显式返回一个值,
None 被作为默认值返回并动态添加了属性 t ,
因此 hasattr(object,name) 的返回结果是 True 。
如果在上述例子中抛出异常( raiseTypeError('unknown attr:' + name) ),
则一切将如用户期待的那样。
__getattr__()和__getattribute__() 的两点提醒:
1)覆盖了__getattribute__()方法之后,任何属性的访问都会调用用户定义的__getattribute__()方法,
性能上会有所损耗,比使用默认的方法要慢。
2)覆盖的__getattr__()方法如果能够动态处理事先未定义的属性,可以更好地实现数据隐藏。
因为 dir() 通常只显示正常的属性和方法,因此不会将该属性列为可用属性,
上述例子中如果动态添加属性y,即使 hasattr(a,y) 的值为 True , dir(a) 得到的却是如下输出;
['__class__','__delattr__','__dict__','__doc__','__format__','__getattr__','__getattribute__','__hash__','__init__','__module__','__new__','__reduce__','__reduce_ex__','__repr__','__setattr__','__sizeof__','__str__','__subclasshook__','__weakref__','name','x',]
再来思考一个问题:property 也能控制属性的访问,
如果一个类中同时定义了 property.__getattribute__() 以及 __getattr__() 来对属性进行访问控制,
那么具体的査找顺序是怎样的呢?
class A(object):
_c= "test"
def __init__(self):
self.x =None
@property
def a(self):
print("using property to access attribute")
if self.x is None:
print("return value")
return 'a'
else:
print("error occured")
raise AttributeError
@a.setter
def a(self,value):
self.x=value
def __getattr__(self,name):
print("using __getattr__ to access attribute")
print("attribute name:",name)
return "b"
def __getattribute__(self,name):
print("using __getattribute__ to access attribute")
return object.__getattribute__(self,name)
上述程序的输出如下:
a1=A()
print (a1.a)
using __getattribute__ to access attribute using property to access attribute using __getattribute__ to access attribute return value a
print("---------------")
---------------
a1.a =1
print(a1.a)
using __getattribute__ to access attribute using property to access attribute using __getattribute__ to access attribute error occured using __getattr__ to access attribute attribute name: a b
print("-------------")
-------------
print (A._c)
test
当实例化al时由于其默认的属性x为 None ,当访问 al.a 时,最先捜索的是__getattribute__()方法,
由于a是一个 property 对象,并不存在于 al 的 diet 中,因此并不能返回该方法,
此时会搜索 property 中定义的 get() 方法,所以返回的结果是a。
当用 property 中的 set() 方法对x进行修改并再次访问 property 的 get() 方法时会抛出异常,
这种情况下会触发对__getattr__()方法的调用并返回结果b。
程序最后访问类变量输出 test 是为了说明对类 变量的访问不会涉及__getattribute__()和__getattr__()方法。
注意:__getattribute__()总会被调用,而__getattribute__()中引发异常的情况下才会被调用。