python对象属性惰性取值

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属性了
posted @ 2018-12-04 17:20  村口王铁匠  阅读(675)  评论(0)    收藏  举报