使用属性类型创建描述符时,出现递归错误

时间:2019-02-14 12:40:21

标签: python python-3.x properties

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def fget(self):
        print("Getting ")
        return self.age

    def fset(self, value):
        print("Setting ")
        self.age = value

    def fdel(self):
        print("Deleting")
        del self.age

    age = property(fget, fset, fdel, "I'm the property.")

if __name__ == "__main__":

    p = Person("ankit", 29)

我正在尝试使用property构造函数来创建描述符。我不想使用property的修饰符形式。我收到错误提示

RecursionError: maximum recursion depth exceeded while calling a Python object

3 个答案:

答案 0 :(得分:2)

导入文件时,方法中的代码将转换为代码对象,但在您实际调用该方法之前,该方法不会执行。在您运行__init__时,age已经是您的媒体资源的名称。因此,行self.age = age会尝试调用fset。但是随后fset尝试做self.age = value,它只是再次调用fset,等等。

问题在于您在实例中没有名为age的可见属性,而在类中只有一个描述符。有两种常见的解决方案。

  1. 最常看到的是在_age上添加一个名为self的属性。尽管Python中没有隐私,但下划线要礼貌地要求用户不要搞乱它,除非他们想要意外的结果。

    在这种情况下,您的课程如下所示:

    class Person:
        def __init__(self, name, age):
            self.name = name
            self._age = age
    
        def fget(self):
            print("Getting ")
            return self._age
    
        def fset(self, value):
            print("Setting ")
            self._age = value
    
        def fdel(self):
            print("Deleting")
            del self._age
    
        age = property(fget, fset, fdel, "I'm the property.")
    
  2. 我个人最喜欢做同一件事的方法是实际使用属性描述符遮盖实例属性的事实。请记住,仅当您使用.表示法(或直接调用__getattr__)时才会发生阴影。您仍然可以很好地分配给self.__dict__。这样就可以以不容易看到或访问的方式保存特定于实例的数据,而无需在对象的名称空间中添加任何名称:

    在这种情况下,您的课程如下所示:

    class Person:
        def __init__(self, name, age):
            self.name = name
            self.__dict__['age'] = age
    
        def fget(self):
            print("Getting ")
            return self.__dict__['age']
    
        def fset(self, value):
            print("Setting ")
            self.__dict__['age'] = value
    
        def fdel(self):
            print("Deleting")
            del self.__dict__['age']
    
        age = property(fget, fset, fdel, "I'm the property.")
    

答案 1 :(得分:2)

object.__setattr__()找到绑定描述符(使用__set__方法的描述符)时,它将调用该描述符,而不是在实例的__dict__中设置name:value对-这很明显实际预期的是xD。

因此,就您而言,当您实例化Person时:

Person("ankit", 29) 

Python调用初始化程序:

def __init__(self, name, age):
    self.name = name
    self.age = age

调用Person.age.__set__(self, age),调用fset(self, value)

def fset(self, value):
    print("Setting ")
    self.age = value

调用Person.age.__set__(self, age),调用fset(self, value)

def fset(self, value):
    print("Setting ")
    self.age = value

调用Person.age.__set__(self, age),调用fset(self, value)

def fset(self, value):
    print("Setting ")
    self.age = value

调用Person.age.__set__(self, age),调用fset(self, value)

def fset(self, value):
    print("Setting ")
    self.age = value

调用Person.age.__set__(self, age),调用fset(self, value)

def fset(self, value):
    print("Setting ")
    self.age = value

等等等等等...

IOW,property和基础实例属性不能使用相同的名称,因为property将完全隐藏实例属性。

通常,人们使用与property相同名称的实现属性,只是在其前加一个下划线(Python的“保护属性”约定):

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def fget(self):
        print("Getting ")
        return self._age

    def fset(self, value):
        print("Setting ")
        self._age = value

    def fdel(self):
        print("Deleting")
        del self._age

    age = property(fget, fset, fdel, "I'm the property.")

答案 2 :(得分:1)

您需要更改property变量的名称...

错误:

  

RecursionError:调用Python对象时超出了最大递归深度

这是因为您调用了property = age,所以递归发生在fset

这里是一个例子:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def fget(self):
        print("Getting ")
        return self.age

    def fset(self, value):
        print("Setting ")
        self.age = value

    def fdel(self):
        print("Deleting")
        del self.age

    my_age = property(fget, fset, fdel, "I'm the property.")

if __name__ == "__main__":

    p = Person("ankit", 29)
    print(p.my_age)

这将打印:

  

获取

     

29