【问题标题】:@properties and public attribute@properties 和公共属性
【发布时间】:2018-02-10 12:30:05
【问题描述】:

我正在学习关于 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 被定义为public,但在@property@x.setter 修饰的函数中却像self.__x 的私​​有属性一样被访问?

【问题讨论】:

  • x() 方法定义了.x 的作用,例如P。如果他们访问self.x,他们将调用自己。

标签: python python-3.x oop properties


【解决方案1】:

这并不是那么简单,因为它严重依赖 Python 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,因为以__ 开头且不以__ 结尾的变量是名称混乱的):

>>> 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 将通过描述符协议 any 拦截对self 上的x 的访问。因此,您不能使用名称 xactual 值存储在实例上,因为它总是会进入 @property@x.setter (还有 x.deleter 但你没有t 实现了该类的一个)功能。所以你必须使用另一个名字来存储变量。

它通常以相同的名称但一个前导下划线存储(也便于维护)。实际上,使用两个前导下划线并不是一个好习惯,因为这会导致难以对类进行子类化和修改 x 属性 - 无需自己对变量名进行名称修改。

【讨论】:

  • 非常感谢您的回答,即使是初学者也可以理解!
  • 只是为了确保我做对了:P类的属性对象x重新定义了由语法.x触发的描述符协议,__get__重定向到x(self)' and __set__`重定向到x(self,x)。在__init__ 中对self.x = x 的调用不是公共属性x 的声明,而是对x(self,x) 的调用,然后定义了存储值的实例的受保护属性_x。这就是为什么p.__dict__ 包含'_x' = 100 而不是'x' = 100 以及为什么tpye(P.x) = &lt;class 'property'&gt;type(p.x) = &lt;class 'int'&gt;
  • 是的,这似乎是正确的。但是它不像@property 实际上重新定义了 描述符协议。只是它使用@property 装饰的函数成为数据描述符,而普通方法是非数据描述符,而_x 之类的东西是实例变量(它们在查找期间具有不同的优先级)。但这是分裂的头发,你的评论很好地总结了这个过程:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-20
  • 2013-02-13
  • 1970-01-01
相关资源
最近更新 更多