实例属性的获取和拦截, 仅对实例属性(instance, variable)有效, 非类属性
getattr: 适用于未定义的属性, 即该属性在实例中以及对应的类的基类以及祖先类中都不存在
1. 动态处理事先未定义的属性, 可更好的实现数据隐藏, 当调用dir(obj)时只会显示初始化定义的正常的属性和方法
getattribute: 对于所有属性的访问都会调用该方法, 当属性不存在时会报错
1. 覆盖该方法之后,任何属性的访问都会调用用户自定义的__getattribute__()方法, 性能上会有所损耗.
- 当两个方法同步被重写, 要么在__getattribute__()中显示调用, 要么是触发AttributeError异常时, getattr()才会被调用
- 要避免无穷递归调用
- 如果访问未定义属性, 且在__getattr__中未跑出AttributeError异常或者显性的返回一个值, 则会返回None
参考: 编写高质量代码:改善Python程序的91个建议
class Attribute(object):
def __init__(self, name):
self.name = name
def __getattribute__(self, key):
try:
print('calling __getattribute__.{key}'.format(key=key))
# 调用超类
return super(Attribute, self).__getattribute__(key)
# return object.__getattribute__(self, key)
except KeyError:
return 'default'
except AttributeError as ex: # 捕获了该异常就不用调用__getattr__
print(ex)
def __getattr__(self, key):
# 什么时候被调用:
# 1.当属性不在实例以及基类和祖先类的__dict__
# 2. 当触发AtrributeError异常时(不仅仅是__getattribute__()引发的 AttributeError),porperty中定义的get()方法也会抛出异常
print('calling __getattr__.{key}'.format(key=key))
# return 'default'
测试 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 __getattribute__(self, name):
print('using __getattribute__ to access attribute')
return object.__getattribute__(self, name)
def __getattr__(self, name):
print('using __getattr__ to access attribute')
print('attribute name: ', name)
return 'b'
if __name__ == '__main__':
a = Attribute('atest')
print(a.name)
print(a.test)
# 测试 property, __getattribute__, __getattr__调用顺序
# b = A()
# print(b.a)
# print('-'*50)
# b.a = 10
# print(b.a)
# print('-'*50)
# print(A._c)
defaultdict 和 魔法方法__missing__()
dict中并不存在__missing__()方法, 需要派生子类并重写__missing__()方法
# python 2.5 之前默认要自己重写__missing__方法
# python3以后 __missing__(key) called by __getitem__ for missing key;
class DefaultDict(dict):
def __init__(self, default_factory, *args, **kwargs):
super(DefaultDict, self).__init__(*args, **kwargs)
self.default_factory = default_factory
def __getitem__(self, key):
try:
return dict.__getitem__(self, key)
except KeyError:
return self.__missing__(key)
def __missing__(self, key):
self[key] = value = self.default_factory()
return value