在编写一些程序的时候,灵活的使用@property能使程序更加的pythonic。@property的作用是能够将类中的一个函数变成此类属性来使用。
举一个简单的例子,我们要编写一个圆的类,可以这样写:
1 | class Circle(): |
这样当我们创建一个圆时,能通过半径来表示这个圆的大小:
1 | my_circle = Circle(4) |
但是当我们创建的圆半径变成6时,我们直接修改它的半径属性:
1 | my_circle.radius = 6 |
这样修改以后,圆的直径却不会跟着半径的变化而变化,还需要我们手动去修改它的直径属性,这样用起来十分不方便。
我们想要的是,当我们修改它的半径时直径也会跟着变成相应的2倍,同时我们修改它的直径时,半径会变成相应的1/2。
这是@property的作用就显现出来了,我们可以这样修改代码:
1 | class Circle(): |
重写了圆的类后,我们再来试一下:
1 | 4) my_circle = Circle( |
怎么样,这个圆变的智能了吧。
我们分析一下圆类的代码,@property将挨着下面的diameter函数变成了Circle类的一个属性,访问 my_circle.diameter
时,得到的结果时diameter函数的返回值。下面的 @diameter.setter 装饰的函数,会在 my_circle.diameter = 20
修改diameter属性时被调用。这样我们的圆就能自动地调整半径和直径的大小了。
@property的另一个用法是隐藏类的属性,防止被随意修改。我们知道用双下划线定义的属性不能被外部访问和修改,但是这样做不绝对,双下划线只是修改了属性的名字来达到不能访问的目的,实际上在外部通过修改后的名字还是能访问的到的。
当我们不想让别人访问修改我们的内部变量时,我们可以用@property将真是的变量隐藏起来,就像这样:
1 | class DataSet(): |
这样,当别人在外部访问images时会以为它是一个正常的属性,但是修改它时就会报错 AttributeError: can't set attribute
,因为我们没有定义@images.setter装饰的函数,所以它是不能被修改的。
property 共有3中功能,访问、修改、删除:
1 | class Goods(): |
测试三个方法:
1 | g = Goods() |
上面你可能会疑问,del g.price
不是将price属性删除了吗,怎么下面访问一下还有呢?这是因为del g.price
只是执行一下@price.delete 修饰的方法,你在这个方法里面写什么,它就只执行什么。
看到这里,你可能已经知道@property 的作用和用法了。其实porperty并不只能通过装饰器来用,它还能在类中直接使用,property方法有4个参数:
静态属性 = property( get_函数, set_函数, del_函数, doc )
这4个参数中前三个与上面的访问、修改、删除相对应,最后一个doc参数用作说明用法。
看一个例子就能够明白:
1 | class Goods(): |
上面这个例子与前面那个Goods类所实现的效果相同。
实际开发中,灵活的使用@property可以帮助开发者逐渐完善数据模型。
但是在实际工作中,我们经常接触到的对象,恰恰就是这种接口设计的比较糟糕的,或仅仅充当数据容器的对象。若是代码越写越多、功能越来越膨胀,或是参与项目的人都不考虑长远的维护事宜,就容易出现糟糕的局面。
@property固然有效,但是也不能滥用,如果你发现自己正在不停地编写各种 @property 方法,那恐怕就意味着当前这个类的代码写的确实很差。此时,应该彻底重构该类,而不应该继续修补这套糟糕的设计。
引自:《Effective Python》