29. 使用普通属性而不是GET和SET方法

用过其它编程语言的人在Python中很容易把getter和setter也拿过来用,例如下面代码:

29. 使用普通属性而不是GET和SET方法

虽然以上用法很简单,但是这并不是Python推荐的用法,以上getter和setter的使用如下:

29. 使用普通属性而不是GET和SET方法

看到没,这样的用法太笨拙了,尤其是给属性增加值的时候:

29. 使用普通属性而不是GET和SET方法

这些实用程序方法确实有助于定义类的接口,使封装功能、验证用法和定义边界变得更容易。这也是定义类时的重要目标,以保证随着类的发展变化不会影响类的调用者。

但是在Python中,你几乎从来都不需要定义显示的getter和setter方法。取而代之的是,你应该在最初设计类的时候就讲这些属性设计成最简单的属性,上面的类应该设计成如下形式:

29. 使用普通属性而不是GET和SET方法

这样的话,你再去实现变量值的增加就清晰多了:

29. 使用普通属性而不是GET和SET方法

如果你真的决定在为属性设置值的时候做些什么事情,那么可以考虑@property和setter属性结合起来。在此我为Resistor类定义一个子类,这个类允许我通过修改voltage属性来改变current的值。需要注意的是,为了使代码能够正常工作,setter和getter方法的名字必须要与想要控制的属性同名:

29. 使用普通属性而不是GET和SET方法

此时如果给voltage属性赋值的话就会运行voltage的setter方法,这个方法会更新对应的current属性:

29. 使用普通属性而不是GET和SET方法

在使用setter设置属性值的时候还可以检查传值,例如变更电阻值的时候可以判断电阻值是不是大于0:

29. 使用普通属性而不是GET和SET方法

如果传入一个负数作为电阻值就会出错:

29. 使用普通属性而不是GET和SET方法

同样给构造函数传递负数的话也会出错:

29. 使用普通属性而不是GET和SET方法

这是因为BoundedResistance.__init__调用了Resistor.__init__,而在Resistor.__init__中使用self.ohms = -5对电阻值进行赋值,self.ohms就是现在的新属性,它调用了@ohms.setter,因此才会出现验证失败的问题。

通过使用@property还可以使得属性不能被修改:

29. 使用普通属性而不是GET和SET方法

在上面代码中由于实例化FixedResistance后,对象会有一个_ohms属性,所以再调用setter的时候就会出现异常:

29. 使用普通属性而不是GET和SET方法

@property的最大的确定就是属性相关方法只能在子类间共享,即使相同的代码在不相关的类之间也不能共享。不过还好,Python支持descriptors(后续会讲到,例如descriptors 实现代码复用)实现属性逻辑和其它代码的复用。

最后要说的就是使用@property实现setter和getter的时候,代码里不要混合其它不相关的逻辑,例如在getter里面给其它属性赋值。这一点在其它编程语言中也是一样的。例如,下面代码就是不合理的,和容易让调用者忽略掉voltage也被赋值了:

29. 使用普通属性而不是GET和SET方法

上面代码就会产生很多怪异的行为:

29. 使用普通属性而不是GET和SET方法


分享到:


相關文章: