使用属性对特性进行访问和设置
property 属性
attribute 特性
有一些面向对象的语言支持私有特性。这些特性无法从对象外部直接访问,我们需要编写 getter 和 setter 方法对这些私有特征进行读写操作。
Python 不需要 getter 和 setter 方法,因为 Python 里所有特性都是公开的,使用时全凭自觉。如果你不放心直接访问对象的特性,可以为对象编写 setter 和 getter 方法。 但更具Python 风格的解决方案是使用属性(property)。
下面例子中,首先定义一个 Duck 类,他仅包含一个 hidden_name 特性。我们不希望别人能够直接访问这个特性,因此需要定义两个方法:getter 方法(get_name())和 setter方法(set_name())。我们在每个方法中都添加一个 print() 函数,这样就能方便地知道它们何时被调用。最后,把这些方法设置为 name 属性:
这两个新方法在最后一行之前都与普通的 getter 和 setter 方法没有任何区别,最后一行则把这两个方法定义为了 name 属性。 property() 的第一个参数是 getter 方法,第二个参数是 setter 方法。现在,当你尝试访问 Duck 类对象的 name 特性时,get_name()会被自动调用:
当然,也可以显式调用 get_name() 方法,它就像普通的 getter 方法一样:
当对 name 特性执行赋值操作时,set_name() 方法会被调用:
也可以显式调用 set_name() 方法:
另一种定义属性的方式是使用装饰器(decorator)。下一个例子会定义两个不同的方法,它们都叫 name(),但包含不同的装饰器:
@property, 用于指示 getter 方法
@name.setter, 用于指示 setter 方法
你仍然可以像之前访问特性一样访问 name, 但这里没有显式的 get_name() 和 set_name() 方法:
实际上,如果有人能猜到我们在类的内部用的特性名是 hidden_name,他仍然可以直接通过 fowl.hidden_name 进行读写操作。
在前面几个例子中,我们都使用 name 属性指向类中存储的某一特性(在我们的例子中是 hidden_name)。除此之外,属性还可以指向一个计算结果值。我们来定义一个 Circle (圆)类,它包含 radius(半径) 特性以及一个计算属性 diameter(直径):
创建一个 Circle 对象,并给 radius 赋予一个初值:
可以像访问特性(例如 radius)一样访问属性 diameter:
真正有趣的还在后面。我们可以随时改变 radius 特性的值,计算属性 diameter 会自动根据新的值更新自己:
如果你没有指定某一特性的 setter 属性(@diameter.setter),那么将无法从类的外部对它的值进行设置。这对于那些只读的特性非常有用:
与直接访问特性相比,使用 property 还有一个巨大的优势,如果你改变了某个特性的定义,只需要在类定义里修改相关代码即可,不需要再每一处调用修改。
动动手
下周见
领取专属 10元无门槛券
私享最新 技术干货