Реализуйте абстрактные свойства без переопределения __init__ в дочернем - PullRequest
1 голос
/ 05 января 2020

Я изучаю модуль abc и мне было интересно, возможно ли то, что я хочу сделать.

По сути, каждый ребенок, которого я составляю в базовом классе, должен иметь ТОТ ЖЕ точный __init__. Базовый класс будет иметь несколько абстрактных свойств, которые должны быть определены дочерним элементом. Я бы определил эти абстрактные свойства без необходимости каждый раз переписывать весь __init__.

Пример:

Сначала я попробовал что-то подобное

from abc import ABC,abstractmethod

class test(ABC):

    def __init__(self):
       pass

    @property
    @abstractmethod
    def prop(self):
        pass

class prop_ex(test):

    @property
    def prop(self):
        return "THIS WORKS"
>>> from abc_tests import prop_ex
>>> blah = prop_ex()
>>> blah.prop
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'prop_ex' object has no attribute 'prop'

, это не сработало.

Затем я попытался

from abc import ABC,abstractmethod

class test(ABC):

    def __init__(self):
        self.prop = prop

    @property
    @abstractmethod
    def prop(self):
        pass

class prop_ex(test):
    prop = "THIS WORKS"

    @property
    def prop(self):
        return self._prop

Тест

>>> from abc_tests import prop_ex
>>> blah = prop_ex()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "abc_tests.py", line 13, in __init__
    self.prop = prop
NameError: name 'prop' is not defined

Ничего хорошего тоже нет, поэтому я попытался

from abc import ABC,abstractmethod

class test(ABC):

    def __init__(self):
        pass

    @property
    @abstractmethod
    def prop(self):
        pass

class prop_ex(test):
    self.prop = "THIS WORKS"

    @property
    def prop(self):
        return self._prop

тест

>>> from dunder_tests import prop_ex
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "abc_tests.py", line 39, in <module>
    class prop_ex(test):
  File "abc_tests.py", line 40, in prop_ex
    self.prop = "THIS WORKS"
NameError: name 'self' is not defined

Для последнего, если вы установите точку останова в __init__ родительского элемента и введите dir(self), в котором вы увидите 'prop'.

>>> blah = prop_ex()
> abc_tests.py(14)__init__()
-> self.prop = prop
(Pdb) dir(self)
['__abstractmethods__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', '__weakref__', '_abc_impl', 'prop']

Поэтому я подумал, что это сработает.

Редактировать:

Понятно, я уже слишком усложняю это. Я мог бы просто сделать

from abc import ABC,abstractmethod

class test(ABC):

    def __init__(self):
        pass

    @property
    @abstractmethod
    def prop(self):
        pass

class prop_ex(test):

    prop = "THIS WORKS"

Есть ли проблема с этим?

1 Ответ

1 голос
/ 05 января 2020

Как вы правильно подвели, самым простым решением было бы определить abstractproperty и просто позволить подклассу определить его, вообще не нужно __init__.

Я бы go на шаг вперед и добавьте исключение в базовый класс, просто чтобы убедиться, что на него нет случайного вызова super().

import abc

class test(abc.ABC):
    @property
    @abc.abstractmethod
    def prop(self):
        raise NotImplementedException()

class prop_ex(test):
    prop = "THIS WORKS"

Кстати: ваш первый пример уже работал для меня. Два других не работают, потому что второй подход пытается найти локальную переменную с именем prop, которая не существует, а третий подход ссылается на self в теле класса - но self определяется только в методах / свойствах класса, а не в теле класса.


Кроме того, это не предотвращает переопределение __init__ в подклассах, любой подкласс все еще может изменить __init__.

...