首页 > 解决方案 > 覆盖python中的抽象方法

问题描述

覆盖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)

标签: pythonabstract-methods

解决方案


你正在尝试做的事情只会奏效——但这是一个非常糟糕的主意。

通常,您不希望在覆盖时以不兼容的方式更改方法的签名。这是里氏替换原则的一部分。

在 Python 中,通常有充分的理由违反这一点——继承并不总是与子类型有关。

但是,当您使用 ABC 定义接口时,这显然是关于子类型化的。ABC这是子类和装饰器的唯一目的abstractmethod,所以用它们来表示其他任何东西充其量是高度误导的。


更详细地说:

通过从 继承Agent,您声明任何 的实例都Clever_Agent可以像使用Agent. 这包括能够打电话my_clever_agent.perceive_world(my_observation)。事实上,它不仅包括这些。这就是它的全部含义!如果那个调用总是失败,那么 noClever_Agent是一个Agent,所以它不应该声称是。

在某些语言中,您有时需要伪造接口检查的方式,以便稍后可以将类型切换和/或“动态转换”回实际类型。但在 Python 中,这从来都不是必需的。没有“ Agents 列表”之类的东西,只有任何东西的列表。(除非您使用可选的静态类型检查——但在这种情况下,如果您需要绕过静态类型检查,请不要声明静态类型只是为了给自己设置障碍。)


在 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)

推荐阅读