0

有人可以向我解释为什么 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=''

4

2 回答 2

5

在第一个示例中,您正在创建 property a,它允许您执行以下操作:

self.a               # Get the value
self.a = some_obj    # Set the value

但是在该a属性中,您再次a指的是该属性,通过!这将产生递归问题。self.a

在接下来的示例中,该属性a由变量 支持self._a,从而避免了此递归问题。

于 2012-07-03T18:33:03.923 回答
3

这里的重点是,在 Python 中,一切(包括函数、方法、“属性”——或任何其他描述符——类甚至模块)都是一个对象,并且“数据”和“函数或方法”没有不同的命名空间。IOW,在 Python 中,一个对象具有属性、句点 - 没有“成员数据”或“成员函数”。甚至基类也是属性(它们本身也是对象,所以它们也有属性)。

属性查找规则是(非常简化 - 我不会提及插槽等一些特殊情况):

阅读:

  1. 首先在父类中查找该名称的属性。如果找到并且该属性实现了描述符协议的“get”部分,则调用该属性的__get__方法。
  2. 然后在实例的__dict__
  3. 然后如果类(或父类之一)有一个__getattr__方法,调用它
  4. 然后引发 AttributeError 异常

用于设置:

  1. 首先在父类中查找该名称的属性。如果找到并且该属性实现了描述符协议的“设置”部分,则调用该属性的__set__方法。
  2. 然后将属性存储在实例的__dict__

我提到但没有解释描述符协议。这个协议(如果你来自 Java,协议是一种“隐含的”接口——你不必声明它,只需实现它)说如果一个对象有一个__get__方法,最终有一个__set__方法(这里我再次通过不提及该部分而过度简化__del__)AND是一个类属性,那么当在类的实例上查找时,它的方法将__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具有 (class) 属性的类对象,该属性a是具有fget=Something.get和的属性fset=Something.set。现在当我们实例化时会发生什么Something?初始化器使用val参数调用,并尝试绑定self.a到该参数。属性查找规则(对于“写作”——我们应该说是“绑定”)开始起作用,并注意到Something类对象有一个属性a——作为property类型的实例——实现__set__了协议描述符的一部分。所以查找规则调用Something.a.__set__(theinstance, val),解析为Something.a.fset(theinstance, val),实现为self.a = val。新属性查找,找到a实现描述符协议绑定部分的类属性,调用它,等等……,砰,无限递归。

长话短说:属性是属性是属性;)

请注意,在您的第三个示例中,该set方法尝试设置self._a而不是self.a. 由于该类没有名为 的描述符_a,因此只需使用该名称创建一个实例属性,因此这里没有递归。

有关描述符协议的更多信息,请参见 - http://docs.python.org/reference/datamodel.html#implementing-descriptors - http://wiki.python.org/moin/ComputedAttributesUsingPropertyObjects

如果您想了解 Python 的“方法”到底是什么(提示:function类型实现了描述符协议)以及为什么需要“自我”参数,您也可以阅读以下内容:- http://wiki.python.org/ moin/FromFunctionToMethod

于 2012-07-03T20:06:35.980 回答