我正在关注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
的私有属性访问?
答案 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
将拦截x
上self
的描述符协议任意访问权限。因此,您无法使用名称x
在实例上存储实际值,因为它始终会进入@property
和@x.setter
(以及{ {1}}但你还没有实现那个类的功能。因此,您必须使用其他名称来存储变量。
它通常以相同的名称存储,但是一个前导下划线(也简化了可维护性)。使用两个前导下划线实际上并不是一个好习惯,因为这使得很难对类进行子类化并修改x.deleter
属性 - 没有名称 - 自己修改变量名称。