何时以及如何在python中使用内build函数property()
在我看来,除了一点点语法糖,财产()没有什么好处。
当然,能写ab=2
而不是a.setB(2)
是a.setB(2)
,但隐藏了ab = 2不是一个简单赋值的事实看起来像一个麻烦的秘诀,要么是因为会出现一些意想不到的结果,如ab=2
实际上导致ab
为1
。 或者引发exception。 或者是性能问题。 或者只是困惑。
你能给我一个好的使用它的具体例子吗? (使用它来修补有问题的代码不计算;-)
在像Java这样依赖getter和setter的语言中,他们不应该被期望也不会做任何事情,除非他们说了什么 – 如果x.getB()
做了任何事情,但是返回逻辑属性b
的当前值,或者如果x.setB(2)
做了任何事情,但是需要less量的内部工作来使x.getB()
返回2
。
然而,这种预期的行为没有语言强制的保证 ,也就是编译器对名称以get
或set
开头的方法主体的约束,而是由常理,社会惯例,“风格指南”和testing。
在具有属性(包括但不限于Python的语言集合)的语言中, xb
访问行为和赋值(例如xb = 2
)与例如Java中的getter和setter方法完全相同 :同样的期望,同样缺乏语言执行的保证。
属性的第一场胜利是语法和可读性。 不得不写,例如,
x.setB(x.getB() + 1)
而不是显而易见的
xb += 1
呼喊为复仇神。 在支持属性的语言中,绝对没有理由强迫这个类的用户通过这样的拜占庭样板的旋转,影响他们的代码的可读性,没有任何好处。
特别是在Python中,使用属性(或其他描述符)代替getter和setter有更大的好处:如果当你重新组织你的类以便不再需要底层的setter和getter时,你可以(不用打破类的发布的API)简单地消除了这些方法和依赖于它们的属性,使得b
的正常“存储”属性,而不是一个“逻辑”的属性获得和计算设置。
在Python中,直接使用(如果可行)而不是通过方法进行处理是一个重要的优化,系统地使用属性可以在任何可行的情况下执行这种优化(总是直接暴露“正常存储属性”,只有那些需要计算和/或通过方法和属性进行设置)。
因此,如果使用getter和setter来代替属性,那么除了影响用户代码的可读性以外, 还会无谓地浪费机器周期(以及在这些周期中传递给计算机的能量;-), 同样没有很好的理由任何。
你唯一的反对财产的论点是,例如“外部用户不会指望作为一个任务的结果,通常”任何副作用; 但是你却错过了这样一个事实:同样的用户(用诸如Java的语言,getter和setter普遍存在)不会因为调用setter而产生(可观察到的)“副作用”(对于getter ;-)。 他们是合理的期望,作为class级作者,你要尝试和适应他们 – 不pipe你的二传手和吸气剂是直接使用还是通过财产使用都没有区别。 如果您的方法具有重要的可观察副作用,请不要将它们命名为getThis
, setThat
,也不要通过属性使用它们。
对“隐藏实现”的抱怨是完全没有道理的:OOP的大部分都是关于实现信息隐藏 – 使一个类负责向外界展示一个逻辑接口并尽可能在内部实现它。 吸气剂和吸附剂,就像属性一样,是实现这一目标的工具。 属性只是做得更好(在支持他们的语言中);-)。
这个想法是让你避免写入getter和setter,直到你真正需要它们。
所以,开始写你:
class MyClass(object): def __init__(self): self.myval = 4
显然你现在可以写myobj.myval = 5
。
但后来,你决定你需要一个二传手,因为你想同时做一些聪明的事情。 但是你不需要改变所有使用你的类的代码 – 所以你把setter包装在@property
装饰器中,这一切都可以工作。
但隐瞒事实,ab = 2不是一个简单的任务看起来像麻烦的配方
你并没有隐藏这个事实, 这个事实从来没有开始。 这是python – 高级语言; 不是assembly。 几乎没有“简单”的陈述归结为单个CPU指令。 阅读简单到一个任务是阅读不在那里的东西。
当你说xb = c时,大概你应该考虑的是“无论发生什么事,xb现在应该是c”。
一个基本的原因实际上只是它看起来更好。 这是更pythonic。 特别是对图书馆。 something.getValue()看起来不如something.value好
在plone(一个很大的CMS)中,你曾经有过document.setTitle(),它做了很多事情,比如存储值,重新索引等等。 只是做document.title ='东西'更好。 你知道,无论如何,幕后发生了很多事情。
你是对的,它只是句法糖。 根据你对有问题的代码的定义,可能没有什么好的用法。
考虑你有一个在你的应用程序中被广泛使用的类Foo。 现在这个应用程序已经变得相当大,并且可以说这是一个已经变得非常stream行的web应用程序。
你认为Foo正在造成瓶颈。 也许可以给Foo添加一些caching来加速。 使用属性可以让你做到这一点,而不会改变任何代码或testing以外的Foo。
当然,这是有问题的代码,但你只是保存了很多$$快速修复它。
如果Foo在图书馆里有数百或数千个用户呢? 那么当你升级到最新版本的Foo的时候,你必须告诉他们做一个昂贵的重构。
发行说明中有关于Foo的lineitem而不是段落移植指南。
有经验的Python程序员除了ab==2
之外别指望ab=2
ab==2
,但他们甚至知道这可能不是真的。 课堂内部发生的事情是自己的事情。
这是我的一个老例子。 我包装了一个C函数库,它具有“void dt_setcharge(int atom_handle,int new_charge)”和“int dt_getcharge(int atom_handle)”的function。 我想在Python级别上做“atom.charge = atom.charge + 1”。
“财产”装饰使得这一点很容易。 就像是:
class Atom(object): def __init__(self, handle): self.handle = handle def _get_charge(self): return dt_getcharge(self.handle) def _set_charge(self, charge): dt_setcharge(self.handle, charge) charge = property(_get_charge, _set_charge)
10年前,当我编写这个包的时候,我不得不使用__getattr__和__setattr__,这使得它成为可能,但是实现更容易出错。
class Atom: def __init__(self, handle): self.handle = handle def __getattr__(self, name): if name == "charge": return dt_getcharge(self.handle) raise AttributeError(name) def __setattr__(self, name, value): if name == "charge": dt_setcharge(self.handle, value) else: self.__dict__[name] = value
getter和setter被用于多种目的,并且非常有用,因为它们对代码是透明的。 拥有对象某些属性的高度,你指定一个值为Something.height = 10,但如果高度有一个getter和setter,那么在你分配这个值的时候,你可以在这个过程中做很多事情,比如validationmin或者max值,就像触发一个事件,因为高度发生了变化,自动设置其他值作为新高度值的函数,所有可能发生在Something.height值赋值的时刻。 请记住,您不需要在代码中调用它们,它们在读取或写入属性值时自动执行。 在某种程度上,它们就像事件过程一样,当属性X改变值和读取属性X值时。