python面向对象进阶
在知乎上面看到这个如何让python对象属性具有惰性求值的能力,认真学习了一番,记录一下。
__setattr__、__getattr__、__delattr__
class Computer(object):
def __init__(self, brand, type):
self.brand = brand
self.type = type
# def __getattribute__(self, item):
# print("----> reference getattribute begin")
# super(Computer, self).__getattribute__(item)
def __getattr__(self, item):
print("----> reference getattr begin")
print("item: {0}".format(item))
if item in self.__dict__:
return self.__dict__[item]
else:
raise Exception("no attr {0}".format(item))
def __setattr__(self, key, value):
print("----> reference setattr begin")
print("***key: {0}, value: {1}***".format(key, value))
self.__dict__[key] = value
def __delattr__(self, item):
print("----> reference delattr begin")
self.__dict__.pop(item)
print("----> delattr end")
pc_1 = Computer('thinkpad', "i7")
# print(pc_1.__dict__)
print(pc_1.brand)
# print(pc_1.bran)
pc_1.size = "15.6"
print(pc_1.size)
# 实例化执行init方法,使用点号赋值时会调用__setattr__方法, 不重写这个方法会默认调用父类:父类会执行类似self.__dict__[key] = value
# pc_1.brand 会先直接执行__getattribute__,如果__getattribute__找不到而当前对象有__getattr__方法会去执行__getattr__去找,找不到就返回了
# pc_1.bran, 会首先执行__getattribute__, 如果__getattribute__找不到而当前对象有__getattr__方法会去执行__getattr__去找,找不到就返回了
# 当__getattribute__与__getattr__同时存在,只会执行__getattrbute__,除非__getattribute__在执行过程中抛出异常AttributeError, 也就是说__getattr__只有在使用点调用属性且属性不存在的时候才会触发
__set__、__get__、__del__
# 描述符Str
class Str:
def __get__(self, instance, owner):
print('Str调用')
def __set__(self, instance, value):
print('Str设置...')
def __delete__(self, instance):
print('Str删除...')
class People(object):
name = Str()
def __init__(self, name, age): # name被Str类代理
self.name = name
self.age = age
p1 = People('tony', 18)
# 如果描述符是一个数据描述符(即有__get__又有__set__),那么p1.name的调用与赋值都是触发描述符的操作,于p1本身无关了,相当于覆盖了实例的属性
p1.name = 'tonyj'
print(p1.name)
# 实例的属性字典中没有name,因为name是一个数据描述符,优先级高于实例属性,查看/赋值/删除都是跟描述符有关,与实例无关了
print(p1.__dict__)
del p1.name
利用描述符让类的属性具有惰性求值:
class lazyproperty(object):
def __init__(self, fun):
print("1111111")
self.fun = fun
def __get__(self, instance, owner):
print("instance : {0}, owner: {1}".format(instance, owner))
if instance is None:
return self
value = self.fun(instance)
print("func: {0}, funcname: {1}".format(self.fun, self.fun.__name__))
setattr(instance, self.fun.__name__, value)
return value
class Circle:
area = 222
def __init__(self, radius):
self.radius = radius
@lazyproperty
def area(self):
print('Computing area')
return 3.1415 * self.radius ** 2
c = Circle(5)
print(c.__dict__)
print(c.area)
print(c.__dict__)
print(c.area)
# 个人分析: c.area语法首先会去对象属性里去查找,有则返回没有则继续,因为area已经被装饰器装饰,
# 其实c.area是调用c.lazyproperty(area),又因为lazyproperty是一个类,这时lazyproperty就相当于
# 对象c的一个代理类,这时会触发代理类的__get__方法__get__(self, instance, owner)
# 这里instance就是被代理类的实例,owner就是被代理的类,这里__get__里代码逻辑就是 这里
# value = self.func(instance)执行原函数内容,然后给instance设置值
# setattr(instance, self.fun.__name__, value),这样之后调用c.area时对象c的属性里就已经存在area属性了