При использовании типа свойства для создания дескриптора я получаю ошибку рекурсии - PullRequest
0 голосов
/ 14 февраля 2019
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 голосов
/ 14 февраля 2019

Код в методе преобразуется в объект кода при импорте файла, но не выполняется до тех пор, пока вы на самом деле не вызовете метод.К тому времени, когда вы запустите __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.")
    
0 голосов
/ 14 февраля 2019

Когда object.__setattr__() находит дескриптор привязки (дескриптор с методом __set__), он вызывает его вместо установки пары имя: значение в __dict__ экземпляра - что, очевидно, является ожидаемым на самом деле 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.")
0 голосов
/ 14 февраля 2019

Вам необходимо изменить имя вашего property var ...

Ошибка:

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

...