Может ли метод родительского класса вызвать абстрактный метод, который будет реализован в дочернем классе? - PullRequest
1 голос
/ 06 августа 2020

Если у меня есть родительский класс с двумя методами:

class Parent():
    @abstractmethod
    @staticmethod
    def functionA():
       pass
    
    def functionB():
       return __class__.functionA() + 1

И я реализую дочерний класс:

class Child(Parent):
    def functionA(): # this function is different for each kind of child
        return 3

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

Также, уместно ли здесь использовать __class__?

Ответы [ 2 ]

4 голосов
/ 06 августа 2020

Во-первых, functionB сам должен быть методом класса.

@classmethod
def functionB(cls):
    return cls.functionA() + 1

Во-вторых, вам все равно нужно украсить functionA как метод c в каждом дочернем классе; в противном случае вы заменяете унаследованный метод stati c методом экземпляра.

class Child(Parent):

    @staticmethod
    def functionA():
        return 3
0 голосов
/ 06 августа 2020

Вот рабочая версия:

from abc import ABC, abstractmethod

class Parent(ABC):
    @staticmethod # not needed, but is documentation
    @abstractmethod
    def a(): pass

    @classmethod
    def b(cls): return cls.a() + 1

class Child(Parent):
    @staticmethod
    def a(): return 3

Мы можем протестировать:

>>> c = Child()
>>> c.b()
4

Примечательные вещи:

  • Нам нужно используйте базовый класс ABC (или метакласс ABCMeta), чтобы иметь доступ к декоратору abstractmethod, как описано в документации модуля abc.

  • __class__ не является ключевым словом; это атрибут объектов . Мы не можем просто делать такие вещи, как __class__.a(), потому что нечего получить __class__ из . Мы могли бы решить эту проблему, используя параметр self обычного метода, но на самом деле мы пытаемся добиться поведения, которое не требует экземпляра, но зависит от того, какой производный класс, который мы используем . И это то, для чего предназначен classmethod, и почему существуют отдельные декораторы classmethod и staticmethod.

Когда вы используете classmethod, класс будет передан как параметр, например, как экземпляр передается при использовании обычного метода. Условно мы называем его cls. Для получения дополнительной информации о classmethod см. этот превосходный доклад Раймонда Хеттингера (из команды Python dev) .

  • Наши реализации абстрактного метода также должны быть декорированы в дочерних классах, потому что они по-прежнему не являются обычными методами - поэтому Python нужно знать, чтобы не передавать экземпляр.

  • Украшение staticmethod на абстракт По техническим причинам метод должен быть указан первым перед украшением abstractmethod. Здесь он фактически ничего не делает; abstractmethod уже будет вести себя как staticmethod, когда мы вызываем, например, Parent.a() (нет экземпляра; мы все равно не можем его создать, поскольку это абстрактный класс).

  • Мы также можем использовать classmethod вместо staticmethod для методов a. Это позволило бы потомкам Child наследовать поведение Child без явного написания собственных реализаций a. В этом случае нам нужно явное украшение classmethod для базового абстрактного метода, а не staticmethod; и, конечно, нам нужно будет добавить cls параметров к каждой реализации a.

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