我对python中的面向对象概念很幼稚。在阅读here中的OOP概念时,我遇到了一个例子。你知道吗
class P1:
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
e = P(x = 2100)
f = e.x*2
print(f)
2000
如果我不将变量设为私有的(在P2类的情况下),那么我猜它将运行到一个无限循环中。你知道吗
class P2:
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
e = P(x = 2100) #Infinite loop
为了检查类P2实际上在做什么,我将代码重组为
class P2:
def __init__(self,x):
print('init area1')
self.x = x
@property
def x(self):
print('property area2')
return self.x
@x.setter
def x(self, x):
print('setter area3')
if x < 0:
print('setter area4')
self.x = 0
elif x > 1000:
print('setter area5')
self.x = 1000
else:
print('setter area6')
self.x = x
当我尝试运行P2(x=2100)时,它会给我一个不可阻挡的输出,比如:
init area1
setter area3
setter area5
setter area3
setter area6
setter area3
setter area6
setter area3
setter area6.......
似乎我的程序首先调用了init方法,然后它不断地从setter area 3到setter area 6来回运行。 有人能解释一下吗
幕后发生了什么?程序是如何运行的?
为什么要在这里设置magic private属性,这样程序就不会运行到无限循环中
@property和@x.setter在这里是如何相互关联的?我不能不写@property就写@setter吗
我知道这些都是基本问题,但我浏览了太多的网上资料,没有找到更好的答案。你知道吗
它实际上不是一个好地方使用双下划线名称弄乱。我喜欢那个教程,除了那个细节。您可以使用一个下划线,或任何有效的python标识符,但属性占用的标识符除外,您将看到相同的效果。你知道吗
property
是实现the descriptor protocol的对象。它是一个方便的描述符,用于常见的描述符用例。但是我们可以创建自己的描述符类型。你知道吗基本上,描述符是实现
__get__
、__set__
或__delete__
的任何组合的任何python类型。你知道吗当您执行
some_object.some_attribute
、some_object.some_attribute = value
和del some_object.some_attribute
时,这些将被调用,其中some_attribute
是some_object.__class__
上的描述符。你知道吗因此,考虑一个具体的例子:
描述符拦截属性访问和修改,并删除类的实例,该类将描述符作为属性,以允许各种有趣的内容。你知道吗
注意,描述符属于类:
如果我将一个实例属性设置为与保存该属性的class属性同名,则会出现正常的python阴影行为:
但是我们可以控制它,我们可以实现
__set__
:property
对象只允许您提供在使用property.__get__
、property.__set__
和property.__delete__
时将委托给的函数。docstrings的信息非常丰富,只需在python shell中使用help(property)
:所以无论你用
@property.setter
装饰什么,你都可以想象get被传递给了property(fset=<whatever>)
。所以现在,每当您的实例尝试设置x.some_attribute = value
,其中.some_attribute
是class X:
上的一个属性时,property.__set__
就会被调用,您可以想象它会被x.some_attribute = value
转换成X.some_attribute.__set__(x, value)
所以,为了找到问题的症结所在,为什么要使用无限递归,因为使用
obj.x = val
,其中.x
是一个属性,将调用fset
,但是在fset中使用obj.x = val
,并且fset
再次被调用,这是隐藏的递归。你知道吗@decorator
语法是为了方便起见,总是首先接受getter,但是您可以使用long-form方式只提供setter:我强烈推荐阅读descriptor HOWTO。Spoiler alert,
classmethod
和staticmethod
都是描述符,Python如何神奇地将实例传递给方法(也就是说,所有函数对象都是描述符,当类上的实例访问时,__get__
方法将实例作为第一个参数传递给函数本身!。它还展示了所有这些东西的Python实现,包括如何用纯Python实现property
!你知道吗相关问题 更多 >
编程相关推荐