@properties和public属性

时间:2017-09-01 12:25:14

标签: python python-3.x oop properties

我正在关注python 3的教程,并且有一个简单的例子,我正在努力。

class P:
    def __init__(self,x):
        self.x = x

    @property
    def x(self):
        return self.__x

    @x.setter
    def x(self, x):
        if x < 0:
            self.__x = 0
        elif x > 1000:
            self.__x = 1000
        else:
            self.__x = x

为什么__init__中的属性x被定义为公共属性,但在self.__x@property装饰的函数中被@x.setter的私有属性访问?

1 个答案:

答案 0 :(得分:0)

这不是那么简单,因为它严重依赖于Pythons descriptor protocol,另见Descriptor HOW-TO which refers to property as well。但我会尝试用简单的术语来解释它。

你有一个类(除了由隐式超类object继承的内容以及一些自动包含的东西)2个属性:

>>> P.__dict__
mappingproxy({'__init__': <function __main__.P.__init__>,
              'x': <property at 0x2842664cbd8>})

为了讨论,我删除了自动添加的属性。您可以随时添加或替换属性:

>>> P.y = 1000
>>> P.__dict__
mappingproxy({'__init__': <function __main__.P.__init__>,
              'x': <property at 0x2842664cbd8>,
              'y': 1000})

但是,当您创建实例时,实例将只有一个属性_P__x(插入_P,因为以__开头且不以__结尾的变量是名称-mangled):

>>> p = P(10)
>>> p.__dict__
{'_P__x': 10}

您还可以添加几乎(仅几乎因为描述符协议拦截某些操作 - 见下文)实例的任何属性:

>>> p.y = 100
>>> p.__dict__
{'_P__x': 10, 'z': 100}

这就是描述符协议发挥作用的地方。如果访问实例上的属性,则首先查看实例是否具有该属性。如果实例没有该属性,它将查看该类 - 但是通过描述符协议!因此,当您访问self.x时,这大致相当于:type(self).x.__get__(self)

>>> p.x
10

>>> type(p).x.__get__(p)
10

同样使用self.x = 200设置属性会调用type(self).x.__set__(self, 200)

>>> p.x = 200
>>> p.x
200
>>> type(p).x.__set__(p, 100)
>>> p.x
100

@property将拦截xself的描述符协议任意访问权限。因此,您无法使用名称x在实例上存储实际值,因为它始终会进入@property@x.setter(以及{ {1}}但你还没有实现那个类的功能。因此,您必须使用其他名称来存储变量。

它通常以相同的名称存储,但是一个前导下划线(也简化了可维护性)。使用两个前导下划线实际上并不是一个好习惯,因为这使得很难对类进行子类化并修改x.deleter属性 - 没有名称 - 自己修改变量名称。