转载:http://python.jobbole.com/81967/python
2.x版本须继承object,才能实现。程序员
Python中有个很赞的概念,叫作property,它使得面向对象的编程更加简单。在详细解释和深刻了解Python中的property以前,让咱们首先创建这样一个直觉:为何咱们须要用到property?编程
假设有天你决定建立一个类,用来存储摄氏温度。固然这个类也须要实现一个将摄氏温度转换为华氏温度的方法。一种实现的方式以下:函数
Pythoncode
1对象 2继承 3接口 4字符串 5get |
class Celsius: def __init__(self, temperature = 0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32 |
咱们能够用这个类产生一个对象,而后按照咱们指望的方式改变该对象的温度属性:
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 |
>>> # create new object >>> man = Celsius()
>>> # set temperature >>> man.temperature = 37
>>> # get temperature >>> man.temperature 37
>>> # get degrees Fahrenheit >>> man.to_fahrenheit() 98.60000000000001 |
这里额外的小数部分是转换成华氏温度时因为浮点运算偏差形成的(你能够在Python解释器中试试1.1 + 2.2)。每当咱们赋值或获取任何对象的属性时,例如上面展现的温度,Python都会从对象的__dict__
字典中搜索它。
Python
1 2 |
>>> man.__dict__ {'temperature': 37} |
所以,man.temperature在其内部就变成了man.__dict__['temperature']
如今,让咱们进一步假设咱们的类在客户中很受欢迎,他们开始在其程序中使用这个类。他们对该类生成的对象作了各类操做。有一天,一个受信任的客户来找咱们,建议温度不能低于-273摄氏度(热力学的同窗可能会提出异议,它其实是-273.15),也被称为绝对零。客户进一步要求咱们实现这个值约束。做为一个以争取客户满意度为己任的公司,咱们很高兴地遵从了建议,发布了1.01版本,升级了咱们现有的类。
对于上边的约束,一个很容易想到的解决方案是隐藏其温度属性(使其私有化),而且定义新的用于操做温度属性的getter和setter接口。能够这么实现:
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class Celsius: def __init__(self, temperature = 0): self.set_temperature(temperature)
def to_fahrenheit(self): return (self.get_temperature() * 1.8) + 32
# new update def get_temperature(self): return self._temperature
def set_temperature(self, value): if value < -273: raise ValueError("Temperature below -273 is not possible") self._temperature = value |
从上边能够看出,咱们定义了两个新方法get_temperature()
和set_temperature()
,此外属性temperature也被替换为了_temperature
。最前边的下划线(_)用于指示Python中的私有变量。
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
>>> c = Celsius(-277) Traceback (most recent call last): ... ValueError: Temperature below -273 is not possible
>>> c = Celsius(37) >>> c.get_temperature() 37 >>> c.set_temperature(10)
>>> c.set_temperature(-300) Traceback (most recent call last): ... ValueError: Temperature below -273 is not possible |
这个更新成功地实现了新约束,咱们再也不容许设置温度低于-273度。
请注意,Python中其实是没有私有变量的。有一些简单的被遵循的规范。Python自己不会应用任何限制。
Python
1 2 3 |
>>> c._temperature = -300 >>> c.get_temperature() -300 |
但这样并不会让人很放心。上述更新的最大问题是,全部在他们的程序中使用了咱们先前类的客户都必须更改他们的代码:obj.temperature改成obj.get_temperature(),全部的赋值语句也必须更改,好比obj.temperature = val改成obj.set_temperature(val)。这样的重构会给那些拥有成千上万行代码的客户带来很大的麻烦。
总而言之,咱们的更新是不向后兼容地。这就是须要property闪亮登场的地方。
对于上边的问题,Python式的解决方式是使用property。这里是咱们已经实现了的一个版本:
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class Celsius: def __init__(self, temperature = 0): self.temperature = temperature
def to_fahrenheit(self): return (self.temperature * 1.8) + 32
def get_temperature(self): print("Getting value") return self._temperature
def set_temperature(self, value): if value < -273: raise ValueError("Temperature below -273 is not possible") print("Setting value") self._temperature = value
temperature = property(get_temperature,set_temperature) |
咱们在get_temperature()
和set_temperature()
的内部增长了一个print()函数,用来清楚地观察它们是否正在执行。代码的最后一行,建立了一个property对象temperature。简单地说,property将一些代码(get_temperature
和set_temperature
)附加到成员属性(temperature)的访问入口。任何获取temperature值的代码都会自动调用get_temperature()
,而不是去字典表(__dict__
)中进行查找。一样的,任何赋给temperature值的代码也会自动调用set_temperature()
。这是Python中一个很酷的功能。咱们实际演示一下。
Python
1 2 |
>>> c = Celsius() Setting value |
从上边的代码中咱们能够看到,即便当咱们建立一个对象时,set_temperature()
也会被调用。你能猜到为何吗?缘由是,当一个对象被建立时,__init__()
方法被调用。该方法有一行代码self.temperature = temperature。这个任务会自动调用set_temperature()
方法。
Python
1 2 3 |
>>> c.temperature Getting value 0 |
一样的,对于属性的任何访问,例如c.temperature,也会自动调用get_temperature()
方法。这就是property所做的事情。这里有一些额外的实例。
Python
1 2 3 4 5 6 |
>>> c.temperature = 37 Setting value
>>> c.to_fahrenheit() Getting value 98.60000000000001 |
咱们能够看到,经过使用property,咱们在不须要客户代码作任何修改的状况下,修改了咱们的类,并实现了值约束。所以咱们的实现是向后兼容的,这样的结果,你们都很高兴。
最后须要注意的是,实际温度值存储在私有变量_temperature
中。属性temperature是一个property对象,是用来为这个私有变量提供接口的。
在Python中,property()是一个内置函数,用于建立和返回一个property对象。该函数的签名为:
Python
1 |
property(fget=None, fset=None, fdel=None, doc=None) |
这里,fget是一个获取属性值的函数,fset是一个设置属性值的函数,fdel是一个删除属性的函数,doc是一个字符串(相似于注释)。从函数实现上看,这些函数参数都是可选的。因此,能够按照以下的方式简单的建立一个property对象。
Python
1 2 |
>>> property() <property object at 0x0000000003239B38> |
Property对象有三个方法,getter(), setter()和delete(),用来在对象建立后设置fget,fset和fdel。这就意味着,这行代码:temperature = property(get_temperature,set_temperature)能够被分解为:
Python
1 2 3 4 5 6 |
# make empty property temperature = property() # assign fget temperature = temperature.getter(get_temperature) # assign fset temperature = temperature.setter(set_temperature) |
它们之间是相互等价的。
熟悉Python中装饰器decorator的程序员可以认识到上述结构能够做为decorator实现。咱们能够更进一步,不去定义名字get_temperature和set_temperature,由于他们不是必须的,而且污染类的命名空间。为此,咱们在定义getter函数和setter函数时重用名字temperature。下边的代码展现如何实现它。
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class Celsius: def __init__(self, temperature = 0): self._temperature = temperature
def to_fahrenheit(self): return (self.temperature * 1.8) + 32
@property def temperature(self): print("Getting value") return self._temperature
@temperature.setter def temperature(self, value): if value < -273: raise ValueError("Temperature below -273 is not possible") print("Setting value") self._temperature = value |
上边的两种生成property的实现方式,都很简单,推荐使用。在Python寻找property时,你极可能会遇到这种相似的代码结构。