利用类的描述符完成一个自定制的property

本文深入探讨Python中描述符的使用方法,特别是通过自定义实现property,达到延迟计算的效果。同时介绍了如何利用装饰器和描述符实现property的读取、设置及删除功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

由property的形式可以看出,@property 也是在调用一个装饰器,所以,装饰器不光可以是一个函数,也可以是一个类

#默认的property
class Room:
    def __init__(self, name, width, length):
        self.name = name
        self.width = width
        self.length = length

    @property
    def cal_area(self):
        return self.width * self.length


r1 = Room("卧室", 20, 20)
print(r1.cal_area)

自定制实现property

class Lazyproperty:
    def __init__(self, func):
        self.func = func

    def __get__(self, instance, owner):
        if instance == None: #注意到类调用被property修饰的属性时会返回property object,所以判断instance是否为None(类调用是instance为None)来判断是否返回Lazyproperty的实例自己
        	return self
        print("运行啦")
        return self.func(instance)#注意这里必须要有instance参数,因为不是实例调用不会自动传参
        # return instance.width * instance.length # 这种写法相当于再写一遍


class Room:
    def __init__(self, name, width, length):
        self.name = name
        self.width = width
        self.length = length

    @Lazyproperty # 本质上是area = Lazyproperty(area) 这也是Lazyproperty为什么必须有__init__函数的原因(其实本质上还是一个描述符)
    def cal_area(self):
        return self.width * self.length


r1 = Room("卧室", 20, 20)
print(r1.cal_area)

类的描述符实现延时计算
需求:需要计算的内容只计算一次,之后都直接调用

class Lazyproperty:
    def __init__(self, func):
        self.func = func

    def __get__(self, instance, owner):
        if instance == None:
            return self
        print("运行啦")
        res = self.func(instance)
        setattr(instance, "cal_area", res)
        return res


class Room:
    def __init__(self, name, width, length):
        self.name = name
        self.width = width
        self.length = length

    @Lazyproperty
    def cal_area(self):
        return self.width * self.length


r1 = Room("卧室", 20, 20)
print(r1.cal_area)
print(r1.__dict__)

这就实现了只有第一次调用时实例会在类中找到描述符(实例属性>非数据描述符),得出计算结果之后将结果加入实例的属性字典中,之后的每一次调用都直接调用属性字典中的内容。

注意,当在描述符中加入__set__(self, instance, value)方法后,延时计算的功能就消失了,因为数据描述符>实例属性,所以每次调用都会到描述符中执行__get__()方法


property的其他内容
被property修饰的函数不能像普通函数一样修改和删除,需要为该函数的修改和删除定义专门的函数

class Room:
    def __init__(self, name, width, length):
        self.name = name
        self.width = width
        self.length = length

    @property
    def cal_area(self):
        return self.width * self.length


r1 = Room("卧室", 20, 20)
r1.cal_area = "dsa" #会报错AttributeError: can't set attribute
del r1.cal_area #会报错AttributeError: can't delete attribute

定义专门的修改和删除函数

class Room:
    def __init__(self, name, width, length):
        self.name = name
        self.width = width
        self.length = length

    @property
    def cal_area(self):
        print("get运行")
        return self.width * self.length

    @cal_area.setter
    def cal_area(self, val):
        print("set运行")
        pass

    @cal_area.deleter
    def cal_area(self):
        print("del运行")
        pass



r1 = Room("卧室", 20, 20)
print(r1.cal_area)
r1.cal_area = "dsa"

或者采用以下方式

def get_cal_area(self):
    print("get运行")
    return self.width * self.length


def set_cal_area(self, val):
    print("set运行")


def del_cal_area(self):
    print("del运行")


class Room:
    def __init__(self, name, width, length):
        self.name = name
        self.width = width
        self.length = length

    cal_area = property(get_cal_area, set_cal_area, del_cal_area)


r1 = Room("卧室", 20, 20)
print(r1.cal_area)
r1.cal_area = "dsa"

用法例子

注意这里调用和set函数不要对同一个值进行修改,会导致虽然实例的属性字典中加入了新内容,但是调用该属性时依旧去描述符代理的get函数中得出属性结果

class Good:
    def __init__(self):
        self.original_price = 80
        self.discount = 0.8

    @property
    def price(self):
        new_price = self.original_price * self.discount
        return new_price

    @price.setter
    def price(self, val):
        self.original_price = val

    @price.deleter
    def price(self):
        del self.original_price


obj = Good()
print(obj.price)
obj.price = 200
print(obj.price)
del obj.price

描述符总结

描述符可以实现大部分python类特性中的底层结构,包括@calssmethod @staticmethod @property和__slot__属性等

描述符是很多高级库和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值