Как я могу забить собственность в пределах подкасса? - PullRequest
0 голосов
/ 04 октября 2018

Я пытаюсь создать класс Config Python 3, в котором пользователи устанавливают параметры конфигурации в качестве атрибутов простого класса в суперклассе или в качестве свойств суперкласса, которые пользователи могут устанавливать для переопределения / тени в экземплярах (sub)учебный класс.Я бы предпочел не требовать от пользователей определения методов установки для атрибутов, которые были созданы с помощью методов свойств.Есть ли способ, которым я могу перекрыть набор атрибутов в родительском классе, даже если он был создан с помощью декоратора свойств?

class Configuration:
    param1 = 'a'

    @property
    def param2(self):
        return self.param1 + 'x'

class Proj1(Configuration):
    param3 = 'blah'

config = Proj1()
config.param2 = 'new_val'

Этот фрагмент кода выдает AttributeError с попыткой присвоения pram2 attrbute из-за свойства декорации в суперклассе.Есть ли способ, которым я могу легко замять это существующее свойство и затенять его произвольным значением атрибута?

Ответы [ 2 ]

0 голосов
/ 04 октября 2018

Чтобы уточнить, что я имею в виду под определением установщика в базовом классе, рассмотрим следующее:

class Configuration:

    param1 = "a"

    @property
    def param2(self):
        return getattr(self, "_param2", self.param1 + "x")

    @param2.setter
    def param2(self, value):
        self._param2 = value


class Proj1(Configuration):

    param3 = "blah"

config = Proj1()
print(config.param2)  # ax
config.param2 = "new_val"
print(config.param2)  # new_val

И вы можете сделать то же самое для любого атрибута класса, который вы ожидаете переопределить по желанию пользователя.

0 голосов
/ 04 октября 2018

Похоже, ваша цель состоит в том, чтобы сделать производный атрибут неизменным на родительском объекте, но позволить дочерним классам изменять его, не требуя, чтобы дочерние классы определяли @setter для этого.Так что просто сделайте, чтобы родитель определял подкласс дружественные методы получения и установки свойств, которые явно запрещают работать с самим родительским классом:

class Configuration:
    param1 = 'a'

    @property
    def param2(self):
        try:
            return self._param2  # If child has an override, use that value
        except AttributeError:
            return self.param1 + 'x'  # Otherwise use parent default

    @param2.setter
    def param2(self, new_val):
        # Treat as immutable on parent
        if type(self) is Configuration:
            raise AttributeError("can't set attribute")
        # But allow it to be set on child
        self._param2 = new_val  # Or type(self)._param2 = new_val if it should be set on class

    @param2.deleter
    def param2(self):
        # Treat as immutable on parent
        if type(self) is Configuration:
            raise AttributeError("can't delete attribute")
        del self._param2  # Or del type(self)._param2 if it's a class attribute, not instance

Теперь экземпляры вашего ребенка могут клобер, unclobber и т. Д., Но исходный родительский класс можетне может быть изменено (вы можете сделать Configuration._param2 = 'new value' вручную, но это нарушает соглашение о деталях реализации для имен с префиксом подчеркивания).

Обратите внимание, что если вам нужно , эти свойства для переноса атрибутов класса,не атрибуты экземпляра (поэтому изменение config.param2 должно изменить param2 для всех других экземпляров Proj1), для согласованности вы бы хотели, чтобы поведение работало и при непосредственном назначении Proj1.param2.Это выглядит ужасно, поскольку определение property, которое работает с прямым доступом к атрибуту класса через класс, а не экземпляр, требует метакласса.Я не буду вдаваться в подробности здесь, но вы можете прочитать больше в нижней части мой ответ на вопрос, который требовал аналогичного решения .

...