Доступ к базовому классу при переопределении - PullRequest
1 голос
/ 27 мая 2019

Учитывая следующие основные классы Python:

class Foo(object):

    def __init__(self, a1):
        self._att1 = a1

    @property
    def att1(self):
        return self._att1

    @att1.setter
    def att1(self, a):
        try:
            self._att1 = a
        except TypeError:
            # handle exception
            print("Not allowed")

class Bar(Foo):
    def __init__(self):
        Foo.__init__(self, 100)

    @Foo.att1.setter
    def att1(self, a):
        # self.att1 = a * 0.5  # RecursionError: maximum recursion depth exceeded
        # Foo.att1 = a * 0.5  # attribute is not changed (base instances are though (?))
        # Foo.att1(self,a)  # TypeError: 'property' object is not callable
        super().att1.__set__(a * 0.5)   # AttributeError: 'int' object has no attribute '__set__'
        # ... some other additional code ...

a = Foo(5)
b = Bar()
b.att1 = 700

print(a.att1)
print(b.att1)

Какой будет синтаксис для вызова установщика базового свойства изнутри его переопределения в дочернем классе? Я понимаю, что могу установить self._att1 напрямую, но я бы хотел этого избежать, потому что тогда мне нужно было бы повторить код обработки исключений. Это простой пример, и у меня есть более сложные случаи, когда базовый класс реализует некоторые дополнительные операции над атрибутом, и я хотел бы избежать повторения того же кода в установщике свойств производного класса.

1 Ответ

3 голосов
/ 27 мая 2019

Код:

class Foo:
    def __init__(self, a1):
        self._att1 = a1

    @property
    def att1(self):
        return self._att1

    @att1.setter
    def att1(self, a):
        if not isinstance(a, int):
            print("Not allowed")
        else:
            self._att1 = a


class Bar(Foo):
    def __init__(self):
        Foo.__init__(self, 100)

    @Foo.att1.setter
    def att1(self, a):
        Foo.att1.fset(self, a * 2)


c = Bar()
print(c.att1)
c.att1 = 10
print(c.att1)
c.att1 = "some string"

Выход:

100
20
Not allowed

UPD.

Следуя совету @chepner, я решил добавить некоторые объяснения.

Когда вы используете декоратор @Foo.att1.setter, он не работает так, как вы ожидали.

In docs Вы можете увидеть 2 примера объявления свойств: назначьте переменную класса с помощью функции property() и используйте декоратор @property.Оба эти метода равносильны, но сначала я нахожу гораздо более очевидным в случае демонстрации того, как работает предоставленный код.

Перепишем объявления классов с использованием функции property() вместо декораторов:

class Foo:
    def __init__(self, a1):
        self._att1 = a1

    def getatt1(self):
        return self._att1

    def setatt1(self, a):
        if not isinstance(a, int):
            print("Not allowed")
        else:
            self._att1 = a

    att1 = property(getatt1, setatt1)

class Bar(Foo):
    def __init__(self):
        super().__init__(100)

    def setatt1(self, a):
        Foo.att1.fset(self, a * 2)

    att1 = property(Foo.getatt1, setatt1)

Как видите, вы не переопределяете свойство, вы создаете новое с тем же именем, что и свойство теневого базового класса.Вы можете доказать это, используя этот код:

print(f"Foo.att1 is Bar.att1: {Foo.att1 is Bar.att1}")

В обоих объявлениях он вернет False, что означает, что объекты свойств этих классов не одинаковы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...