python中的property特性

property特性解决什么问题: 属性的存取检查 (封装)
Fluent Python中的经典例子就是商品价格设置,商品价格不能为负数,所以设置价格属性的时候必须进行检查。

方法一、如果没有特性,可以怎么做

  • 禁止 ‘.’ 运算符直接访问属性。这个通过私有属性来完成。
  • 设置存取方法。

这也是C++的做法。由于python中没有访问控制符,可以使用双下划线隐藏属性。

class GoodItem:
    
    def __init__(self, price):
        self.set_price(price)
    
    def set_price(self, price):
        if price > 0:
            self.__price = price
        else:
            print("Error happened: the price \
             has to greater than zero")
    
    def get_price(self):
        return self.__price


m1 = GoodItem(10)
print(m1.get_price())
m1.set_price(-1)

方法一已经能达到封装效果,但是python还想要个自行车。
我们希望封装细节这种东西,python在类里面自己解决,对外的时候 obj.attr 还是可行的,而不用调额外的接口。
也就是这样:

print(m1.price) #打印价格
m1.price = -1 #价格设置不合理,报错

所以这就需要特性了。
方法二、创建特性

# 设置属性的方法1
class GoodItem:
    
    def __init__(self, price):
        self.price = price
    
    def set_price(self, price):
        if price > 0:
            self.__price = price
        else:
            print("Error happened: the price \
            has to greater than zero")
    
    def get_price(self):
        return self.__price
    
    price = property(get_price, set_price)
    
    
m2 = GoodItem(10)
print(m2.price)
m2.price = -1

这里注意到几个设置特性的问题
1、特性的名称怎么确定
名称完全由property返回值绑定到哪个变量来决定。

price = property(get_price, set_price)
# wtf = property(get_price, set_price)

上述例子绑定到price,因此调用属性的时候是使用obj.price。
如果我绑定到wtf,那么调用属性的时候就要用obj.wtf。

2、特性的值被存储在哪里
在set_price看到,属性的值实际上是存到了self. __price里面。从外部接口看,我创建了个属性price,实际上这个属性对实例来说并不存在,真正存在的是__price。而特性实际上是个类属性。

3、为什么price没有self,get_price和set_price也没有self
上面说过了特性其实是个类属性,具体是怎么回事详见续集3。至于get_price和set_price为什么没有self,暂时还不清楚。通常来讲调用类中的方法,不管是类、静态还是实例方法,要么使用self,要么使用cls,除非是内部函数,可以直接使用。这方面的问题还没看到相关资料可以解释。

class GoodItem:

    def __init__(self, price):
        self.price = price
    
    @property
    def price(self):
        return self.__price
    
    @price.setter
    def price(self, p):
        if p > 0:
            self.__price = p
        else:
            print("Error happened: the price \
            has to greater than zero")

另一种方法是使用特殊的装饰器(@property)。这时候叫啥取决于@property装饰的方法,这个例子里面是price,所以属性名称就是price,注意这是get方法。并且get和set方法必须是同名的。

一个需要注意的问题:
上面说过特性的值存储位置。这个变量的名称有两个选择:(这真是一句废话)

  • 和特性同名
  • 和特性不同名,这是上述例子的做法,使用__price保存特性的值。
    那么假设我们要同名咋办,拍脑袋一想可能是这样
def price(self, p):
        if p > 0:
            self.price = p #这是个错误示范
        else:
            print("Error happened: the price \
            has to greater than zero")

self.price=p是要调用price方法进行set的,在price如果还用self.price=p,那就是无穷递归了。所以同名情况下self.price=拍这种是不能在set方法里面用的。

self.__dict__[‘price’] = price #这是个正确示范

obj.__dict__保存的是实例的实例属性。这次price真的是实例属性了,但是由于被特性覆盖了,每次调用obj.price其实是由特性price来执行操作,而不是直接存取实例属性price。如果把特性删了,那么实例price就会暴露出来。具体详见2。

续集:
2、属性的存取的内在逻辑
3、property的到底是怎么回事

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值