Переопределение абстрактных методов в Python - PullRequest
0 голосов
/ 24 апреля 2018

При переопределении абстрактного метода Python, есть ли способ переопределить метод дополнительными параметрами в сигнатуре метода?

например,

Абстрактный класс =

Agent(ABC):

    @abstractmethod
    def perceive_world(self, observation):
        pass

Класс наследования:

Dumb_agent(Agent):

    def perceive_world(self, observation):
        print('I see %s' % observation)

Класс наследования с дополнительным параметром в сигнатуре метода:

Clever_Agent(Agent):

    def perceive_world(self, observation, prediction):
        print('I see %s' % observation)
        print('I think I am going to see %s happen next' % prediction)

Ответы [ 3 ]

0 голосов
/ 24 апреля 2018

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

Однако это может привести к нарушению принципа может привести к ошибкам и проблемам дизайна.В общем, желательно, чтобы подкласс использовался каждый раз, когда суперкласс пригоден для использования.То есть каждый раз, когда метод или функция хотят получить Agent, вы должны иметь возможность передать CleverAgent.К сожалению, если CleverAgent принимает дополнительные параметры, то любой код, который вызывает perceive_world на Agent, потерпит неудачу, если ему дан CleverAgent.

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

Кроме того, в некоторых случаях выВы используете подклассы и где принцип подстановки Лискова не является желаемым свойством.Такие случаи обычно означают, что вы используете механизм наследования объектов языка для каких-то целей, кроме простого подтипа.Если вы окажетесь в такой ситуации, это хороший совет, что вы должны проверить свой проект на тройную проверку и посмотреть, есть ли подход, который лучше выровнен.

0 голосов
/ 24 апреля 2018

То, что вы пытаетесь сделать, просто сработает, но это очень плохая идея.

В общем, вы не хотите изменять сигнатуру метода несовместимыми способами при переопределении.Это часть принципа замены Лискова .

В Python часто есть веские причины нарушать это - наследование не всегда связано с подтипом.

Но когда вы 'Использование ABC для определения интерфейса, это явно подтип.Это единственная цель ABC подклассов и abstractmethod декораторов, поэтому использование их для обозначения чего-либо еще в лучшем случае вводит в заблуждение.


Более подробно:

Путем наследования отAgent, вы заявляете, что любой экземпляр Clever_Agent может использоваться, как если бы это был Agent.Это включает в себя возможность звонить my_clever_agent.perceive_world(my_observation).На самом деле, это не просто включает это;это все, что это значит!Если этот вызов всегда будет неуспешным, то Clever_Agent не будет Agent, поэтому он не должен претендовать на это.

В некоторых языках вам иногда приходится подделывать свой способ проверки интерфейса, поэтому выПозже можно переключить тип и / или «динамическое приведение» обратно к фактическому типу.Но в Python это никогда не нужно.Нет такого понятия, как «список Agent s», просто список чего-либо вообще.(Если вы не используете дополнительную проверку статического типа - но в этом случае, если вам нужно обойти проверку статического типа, не объявляйте статический тип только для того, чтобы обойти себя.)


В Python вы можете расширить метод за пределы его метода суперкласса, добавив необязательные параметры, и это совершенно правильно, поскольку он по-прежнему совместим с явно объявленным типом.Например, это было бы вполне разумно:

class Clever_Agent(Agent):
    def perceive_world(self, observation, prediction=None):
        print('I see %s' % observation)
        if prediction is None:
            print('I have no predictions about what will happen next')
        else:
            print('I think I am going to see %s happen next' % prediction)

Или даже это может быть разумно:

class Agent(ABC):
    @abstractmethod
    def perceive_world(self, observation, prediction):
        pass

class Dumb_agent(Agent):
    def perceive_world(self, observation, prediction=None):
        print('I see %s' % observation)
        if prediction is not None:
            print('I am too dumb to make a prediction, but I tried anyway')

class Clever_Agent(Agent):
    def perceive_world(self, observation, prediction):
        print('I see %s' % observation)
        print('I think I am going to see %s happen next' % prediction)
0 голосов
/ 24 апреля 2018

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

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

...