python中的属性方法

时间:2012-07-03 18:29:43

标签: python

有人可以解释这个行为,为什么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 =''

2 个答案:

答案 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中,一个对象具有属性,句点 - 没有“成员数据”也没有“成员函数”。甚至基类都是属性(它们本身就是对象,所以它们也有属性)。

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

阅读:

  1. 首先在父类中查找该名称的属性。如果 发现AND此属性实现了描述符协议的“get”部分,调用 该属性的__get__方法。
  2. 然后在实例的__dict__
  3. 中查找实例属性
  4. 然后如果类(或其中一个父类)有__getattr__方法,则将其命名为
  5. 然后引发AttributeError异常
  6. 用于设置:

    1. 首先在父类中查找该名称的属性。如果 发现AND此属性实现描述符协议的“set”部分,调用 该属性的__set__方法。
    2. 然后将该属性存储在实例的__dict__
    3. 我提到但没有解释描述符协议。这个协议(如果你来自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.getfset=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