覆盖python中的抽象方法

时间:2018-04-24 18:07:17

标签: python abstract-methods

当覆盖python抽象方法时,有没有办法在方法签名中使用额外参数覆盖该方法?

e.g。

抽象类=

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 :(得分:2)

你想要做的只是工作 - 但这是一个非常糟糕的主意。

通常,您不希望在覆盖时以不兼容的方式更改方法的签名。这是Liskov Substitution Principle

的一部分

在Python中,通常有很好的理由违反 - 继承并不总是关于子类型。

但是当你使用ABCs来定义一个接口时,那就是明确的子类型。这是ABC子类和abstractmethod装饰器的唯一目的,因此使用它们来表示其他任何内容都充其量具有误导性。

更详细:

通过继承Agent,您宣布Clever_Agent的任何实例都可以像Agent一样使用。这包括能够致电my_clever_agent.perceive_world(my_observation)。事实上,它不只是包含;这完全取决于它的含义!如果该通话始终失败,则Clever_Agent不是Agent,因此它不应声称为。{/ p>

在某些语言中,您偶尔需要伪装一下接口检查,这样您以后可以键入 - 切换和/或“动态转换”回实际类型。但在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)

答案 1 :(得分:0)

在很多方面,从父类重写抽象方法并添加或更改方法签名在技术上不称为方法覆盖您可能实际做的是方法隐藏。方法覆盖始终覆盖父类中的特定现有方法签名。

您可以通过在父类中定义变量抽象方法来找到解决问题的方法,并在子类中根据需要覆盖它。

答案 2 :(得分:-1)

你可以像你提议的那样简单地做:你可以在子类中添加一个额外的参数。

但是,这样做很可能违反substitution principle,并可能导致错误和设计问题。通常,希望每当超类可用时子类都可用。 也就是说,每当方法或函数想要Agent时,您都应该能够传入CleverAgent。 不幸的是,如果CleverAgent需要其他参数,那么在perceive_world上调用Agent的任何代码都会失败。

有时需要将一个可选的参数(一个具有默认值)添加到子类中。如果某些代码要了解子类的特殊行为,并且该代码在与方法交互时希望利用该知识,那么这可能是正确的方法。

此外,在某些情况下,您正在使用子类,而Liskov替换原则实际上并不是所需的属性。这种情况通常意味着您将语言的对象继承机制用于除简单子类型之外的某些目的。如果您发现自己处于这种情况,那么您应该尝试检查您的设计并查看是否有更好的对齐方法,这是一个很好的暗示。