Определите абстрактное свойство чтения-записи, чтобы заставить реализацию getter и setter - PullRequest
2 голосов
/ 27 марта 2020

Документы

Python Документы (версия 3.8):

Приведенный выше пример определяет свойство только для чтения; Вы также можете определить абстрактное свойство для чтения и записи, соответствующим образом пометив один или несколько базовых методов как абстрактные:

class C(ABC):
    @property
    def x(self):
        ...

    @x.setter
    @abstractmethod
    def x(self, val):
        ...

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

Мой код

from abc import ABC, abstractmethod
class C(ABC):
    @property
    def x(self):
        ...

    @x.setter
    @abstractmethod
    def x(self, val):
        ...

class D(C):
    @property
    def x(self):
        pass

d = D()

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

Фактическое поведение : Создает без ошибок

Вопрос

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

Ссылка: https://docs.python.org/3.8/library/abc.html#abc .abstractproperty

1 Ответ

1 голос
/ 13 апреля 2020

В сущности, проблема заключается в том, что метод get и метод set являются частью одного и того же атрибута класса. В @property x есть fget, fset и fdel, которые составляют геттер, сеттер и удалитель (не обязательно все установлено). Они могут быть абстрактными и мешать инициализации, или просто не существовать.

В вашем class D вы создаете новый @property, который полностью переопределяет родительский @property, так что больше нет никакого абстрактного установщика, который бы предотвращал Инициирование класса.

from abc import ABC, abstractmethod
class C(ABC):
    @property
    def x(self):
        pass

    @x.setter
    @abstractmethod
    def x(self, val):
        pass

class D(C):
    @property
    def x(self):
        pass

def examine_property(p):
    print('get', p.fget, getattr(p.fget, '__isabstractmethod__', False) if p.fget is not None else None)
    print('set', p.fset, getattr(p.fset, '__isabstractmethod__', False) if p.fset is not None else None)
    print('del', p.fdel, getattr(p.fdel, '__isabstractmethod__', False) if p.fdel is not None else None)

print("C.x:")
examine_property(C.x)
print("D.x:")
examine_property(D.x)

Вывод:

C.x:
get <function C.x at 0x101ac4550> False
set <function C.x at 0x101ac45e0> True
del None None
D.x:
get <function D.x at 0x101ac4670> False
set None None
del None None

Итак, если вы хотите переопределить геттер, вам нужно очень точно указать c об этом, используя @C.x.getter:

class D(C):
    @C.x.getter
    def x(self):
        print("non-abstract getter")

Вывод:

C.x:
get <function C.x at 0x1022fb550> False
set <function C.x at 0x1022fb5e0> True
del None None
D.x:
get <function D.x at 0x1022fb670> False
set <function C.x at 0x1022fb5e0> True
del None None

Таким образом, вы не переопределяете целое property, а только его конкретную c функцию.

I не будет go смешивать создание новых properties и переопределение родителей, я видел это привередливое поведение в моем тестировании:

class D(C):
    @property
    def x(self):
        pass
    @C.x.setter
    def x(self, val):
        pass
print("D.x:")
examine_property(D.x)

Вывод:

D.x:
get <function C.x at 0x10cdd74c0> False
set <function D.x at 0x10cdd7670> False
del None None
# Our new override property is gone

Переупорядоченное определение класса:

class D(C):
    @C.x.setter
    def x(self, val):
        pass
    @property
    def x(self):
        pass

Выход:

D.x:
get <function D.x at 0x105048670> False
set None None
del None None
# We entirely lost the `setter`
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...