在定义一个类的时候,有时我们需要获取一个类的属性值,而这个属性值需要经过类中的其他属性运算来获得的。那么很容易,只要我们在类中定义一个方法,并且通过调用方法可以获取到那个需要运算的属性值。那么,问题来了,当有一天需求变了,你需要反向操作你之前实现的类,你需要通过传入那个需要运算得来的值来获取参与运算的属性值。显然,我们需要重新定义很多的函数来获取那些属性值。这样的类是很不友好的,其他人在调用你定义的类,需要做大量的修改。那么有没有什么解决的办法呢?python提供了一样东西:特性(property)。property避免了以上的问题,使得调用类的人只要知道类怎么用就可以了,而不用了解它是怎么实现的。这很好的实现了面向对象语言的封装性。
这样说来还是有点抽象,那么到底怎么用呢?我下面以一个例子说明property的用法。
以购买水果为例
先粘贴一段代码:
代码语言:javascript复制class Fruits:
def __init__(self):
self.name = 'apple'
self.percost = 0.5
self.color = 'red'
self.num = 0
def set_money(self,money):
self.num = money/self.percost
def get_money(self):
return self.percost*self.num
money = property(get_money,set_money)
我定义一个水果类,初始化水果的名称为apple,单个价格(percost)为0.5元,个数(num)为0。我还定义了get_money方法,用于获取付钱的金额
接下来我们实例化fruit,并为num赋值为10,即要买十个苹果,那么我们想获得需要付多少钱的时候,只要通过调用get_money就可以了。但是奇怪的是,我为什么可以通过直接用fruit.money就可以获得实际的付款金额呢?先别急,接下来慢慢解释。我先往下讲。
读者会发现,我还定义了一个set_money函数和类属性money,那么它们究竟有什么用?从property的参数可以知道,有一个是get_money,就会我们上面想获得的付款金额。通过将get_money传入property函数获得结果赋值给money。那么我们就可以在实例对象中直接通过属性(即fruit.money)的形式来获取付款金额了。
那么set_money又有什么用呢?这就是我文章开头所说的,当有一天需求变了,需要对类的实例对象进行反向操作的时候,我们怎么有效减少代码的数量,提高效率。这个set_money可以使我们在通过fruit.money传入付款金额的时候,接下来通过fruit.num来获取购买的苹果数量。是不是很神奇?
具体的代码实现和结果可以看下面我截出来的两张图。
结果:
一个property函数就可以有如此大的威力,即可以正向操作,由可以反向操作。那么它是如何实现的呢?
通过阅读源码,可以知道,property函数其实质上是一个类,传入的参数有4个,即fget,fset,fdel和doc。分别对应于获取属性值,设置属性值,删除属性值和文档字符串。他们一起定义了所谓的描述符协议。
实际上,这个类包含了一些魔法方法,这些魔法方法为_ _set_ _,_ _get_ _,_ _del_ _。分别在类的属性的设置,获取和删除的时候自动调用。那么可以理解,上面我们定义的get_money,set_money方法,其实内部是调用了上面的魔法方法。当我们在设置fruit.num的时候,自动就会调用set_money方法,那么就会返回我自己写的方法的值,即ruturn self.percost * self.num的结果。反过来,我在设置fruit.money的时候,就会自动调用set_money方法,同样通过我定义的方法,获得了水果的个数,即self.num。那么就可以通过fruit.num轻而易举地获得这个计算出来的个数量了。通过将set_money和get_money方法作为参数传入property函数,我们就可以随时获得想要的结果。在不同的情况获取不同的计算值。
特性property是一个强大的函数,虽然它的内部实现原理很简单,但在实际应用中,笔者认为还是很有用处的。就如我上面所说的需求下,用property可以很好地解决一些问题。更多的用途还需要在实践中去慢慢思考和体会。