何时以及如何在python中使用内build函数property()

在我看来,除了一点点语法糖,财产()没有什么好处。

当然,能写ab=2而不是a.setB(2)a.setB(2) ,但隐藏了ab = 2不是一个简单赋值的事实看起来像一个麻烦的秘诀,要么是因为会出现一些意想不到的结果,如ab=2实际上导致ab1 。 或者引发exception。 或者是性能问题。 或者只是困惑。

你能给我一个好的使用它的具体例子吗? (使用它来修补有问题的代码不计算;-)

在像Java这样依赖getter和setter的语言中,他们不应该被期望也不会做任何事情,除非他们说了什么 – 如果x.getB()做了任何事情,但是返回逻辑属性b的当前值,或者如果x.setB(2)做了任何事情,但是需要less量的内部工作来使x.getB()返回2

然而,这种预期的行为没有语言强制的保证 ,也就是编译器对名称以getset开头的方法主体的约束,而是由常理,社会惯例,“风格指南”和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你的二传手和吸气剂是直接使用还是通过财产使用都没有区别。 如果您的方法具有重要的可观察副作用,请不要将它们命名为getThissetThat ,也不要通过属性使用它们。

对“隐藏实现”的抱怨是完全没有道理的: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值时。