python __getattr__ & __getattribute__ 学习

时间:2023-03-10 00:22:54
python __getattr__ & __getattribute__ 学习

实例属性的获取和拦截, 仅对实例属性(instance, variable)有效, 非类属性

getattr: 适用于未定义的属性, 即该属性在实例中以及对应的类的基类以及祖先类中都不存在

1. 动态处理事先未定义的属性, 可更好的实现数据隐藏, 当调用dir(obj)时只会显示初始化定义的正常的属性和方法

getattribute: 对于所有属性的访问都会调用该方法, 当属性不存在时会报错

1. 覆盖该方法之后,任何属性的访问都会调用用户自定义的__getattribute__()方法, 性能上会有所损耗.

  1. 当两个方法同步被重写, 要么在__getattribute__()中显示调用, 要么是触发AttributeError异常时, getattr()才会被调用
  2. 要避免无穷递归调用
  3. 如果访问未定义属性, 且在__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