有人可以解释这个行为,为什么1)在2)和3)工作时不起作用
1)
class bm(object):
def __init__(self,val):
self.a=val
def get(self):
return self.a
def set(self,val):
self.a=val
a=property(get,set)
In [43]: ob1=bm('vin')
给了我递归错误 ,而下面的代码工作正常
2)
class bm(object):
def __init__(self,val):
self._a=val
def get(self):
return self._a
def set(self,val):
self._a=val
a=property(get,set)
In [43]: ob1=bm('vin')
工作正常。我可以访问ob.a并执行ob.a =''
即使这样也可以正常使用
3)
class bm(object):
def __init__(self,val):
self.a=val
def get(self):
return self._a
def set(self,val):
self._a=val
a=property(get,set)
In [43]: ob1=bm('vin')
工作正常。我可以访问ob.a并执行ob.a =''
答案 0 :(得分:5)
在第一个示例中,您正在创建属性a
,它可以让您执行以下操作:
self.a # Get the value
self.a = some_obj # Set the value
但在a
属性中,您通过a
再次引用self.a
属性 !这会产生递归问题。
在下面的示例中,属性a
由变量self._a
支持,避免了此递归问题。
答案 1 :(得分:3)
这里的要点是,在Python中,所有东西(包括函数,方法,“属性” - 或任何其他描述符 - 类甚至模块)都是一个对象,并且“数据”和“函数”没有明确的命名空间。方法”。 IOW,在Python中,一个对象具有属性,句点 - 没有“成员数据”也没有“成员函数”。甚至基类都是属性(它们本身就是对象,所以它们也有属性)。
属性查找规则(非常简化 - 我不会提到一些特殊情况,如插槽等):
阅读:
__get__
方法。__dict__
__getattr__
方法,则将其命名为用于设置:
__set__
方法。__dict__
我提到但没有解释描述符协议。这个协议(如果你来自Java,一个协议是一种“隐含的”接口 - 你不必声明它,只是为了实现它)说如果一个对象有一个__get__
方法,最后一个__set__
方法(这里我再次通过不提及__del__
部分进行过度简化)并且是一个类属性,然后当查询类的实例时,将调用它的__get__
方法(带“读取”查找的实例和类作为参数),它的__set__
方法将在“写入”上调用(带有实例和值)。
IOW,描述符协议是Python中计算属性的基础。
现在关于property
类型(是的,它是一个类,而不是一个函数):它以一种非常简单的方式实现了描述符协议。以下是纯Python中如何实现属性类型的简化版本(不考虑__del__
部分):
class property(object):
def __init__(self, fget, fset=None, doc=None):
self.fget = fget
self.fset = fset
self.doc = doc
def __get__(self, instance, cls=None):
if not instance:
return self
return self.fget(instance)
def __set__(self, instance, value):
if not self.fset:
raise AttributeError("attribute is read-only")
self.fset(instance, value)
回到问题 - 给出了类声明:
class Something(object):
def __init__(self,val):
self.a=val
def get(self):
return self.a
def set(self,val):
self.a=val
a=property(get,set)
我们有一个具有(类)属性Something
的类对象a
,该属性是fget=Something.get
和fset=Something.set
的属性。现在当我们实现Something
时会发生什么?使用val
参数调用初始化程序,并尝试将self.a
绑定到该参数。属性查找规则(用于“写入” - 我们应该说'绑定')启动并注意到Something
类对象具有属性a
- 作为{{1}的实例} type - 实现协议描述符的property
部分。因此,查找规则会调用__set__
,其解析为Something.a.__set__(theinstance, val)
,其实现为Something.a.fset(theinstance, val)
。新的属性查找,找到实现描述符协议的绑定部分的类属性self.a = val
,调用它,等等...,爆炸,无限递归。
简而言之:属性是属性属性;)
请注意,在您的第三个示例中,a
方法尝试设置set
而不是self._a
。由于该类没有名为self.a
的描述符,因此这只是按该名称创建一个实例属性,因此这里没有递归。
有关描述符协议的更多信息,请参阅 - http://docs.python.org/reference/datamodel.html#implementing-descriptors - http://wiki.python.org/moin/ComputedAttributesUsingPropertyObjects
如果你想了解Python“方法”究竟是什么(提示:_a
类型实现描述符协议)以及为什么需要'self'参数,你也可以阅读:
- http://wiki.python.org/moin/FromFunctionToMethod