__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__()
中引发异常的情况下才会被调用。