对于面向对象编程特别重要的是,关注行为和数据的分离。
在这之前,先来讨论一些“坏”的面向对象理论,这些都告诉我们绝不要直接访问属性(如Java):
class Color: def __init__(self, rgb_value, name): self._rgb_value = rgb_value self._name = name def set_name(self, name): self._name = name def get_name(self): return self._name
前缀有一个单下划线的变量表明他们是类私有的,接着get和set方法提供了对每个变量的访问方式,这个类在实际使用中一般采用如下的方式:
>>> c = Color(‘#ff0000‘, ‘bright red‘) >>> c.get_name() ‘bright red‘ >>> c.set_name(‘red‘) >>> c.get_name() ‘red‘
这并不像python喜欢的直接访问方式具有可读性:
class Color: def __init__(self, rgb_value, name): self.rgb_value = rgb_value self.name = name
调用如下:
>>> c = Color(‘#ff0000‘, ‘bright red‘) >>> print(c.name) bright red >>> c.name = "red" >>> print(c.name) red
Java的这种方式方便在需要这些变量被赋值时添加额外的代码,例如我们想要验证输入值是否合理,则可以改变set_name()方法来实现:
def set_name(self, name):
if not name:
raise Expception("Invalid Name")
self._name = name
但是这样会有一个问题,采用直接访问属性方法的代码,现在必须通过调用方法才能访问原有的属性,如果他们不改变自己的访问方式,那么代码就被破坏了。
而在python中可以使用property关键字来处理该问题,加入我们原本使用直接成员访问的方法取访问属性,之后我们可以增加几个方法,在不改变访问接口的情况下,来对name这个变量进行取值和赋值。
class Color: def __init__(self, rgb_value, name): self.rgb_value = rgb_value self._name = name def _set_name(self, name): if not name: raise Exception("Invalid Name") self._name = name def _get_name(self): return self._name name = property(_get_name, _set_name)
先将name这个属性改为一个(半)私有的_name属性,接着我们添加两个(半)私有方法对这个变量进行取值和赋值,并在赋值的时候进行验证。最后我们在代码底部使用property关键字进行声明。
现在Color类拥有了一个全新的name属性,这个name属性变为了一个property属性,需要通过调用我们刚刚添加的两个方法才能访问或者改变其值。而Color类仍能以前一个版本中相同的方式来使用,同时它还能支持对name赋值时进行验证。
>>> c = Color(‘#ff0000‘, ‘bright red‘) >>> print(c.name) bright red >>> c.name = ‘red‘ >>> print(c.name) red >>> c.name ="" Traceback (most recent call last): File "<stdin>", line 1, in <module> File "color2.py", line 8, in _set_name raise Exception("Invalid Name") Exception: Invalid Name
这样,我们之前编写的任何代码仍然能够工作,但是,即便name变成了property属性,也不能保证100%的安全,如果有人使它设定为空字符串值,仍然可以通过直接访问_name属性的方式来达到目的。
property是怎样工作的
property函数实际上返回了一个对象,该对象通过我们指定的方法代理了全部对属性值访问或赋值的请求。
property构造函数实际上还可以接受两个额外的参数:一个删除函数和一个property的文本字符串。在实际中很少用到删除函数,但是如果需要用到记录所删除的值,那么删除函数还是很有用的。同时在我们满足某个条件的情况下,删除函数还可以否决删除操作。文本字符串是一个用来描述该property的字符串。如果我们不提供文本字符串这个参数,那么该值将从property的第一个参数,也就是getter方法的文本字符串中复制过来。
参考:
1、《Python3 面向对象编程》 [加]Dusty Philips 著