派生类中的签名不匹配比父类更明确

问题描述 投票:0回答:1

我有一个像这样的抽象父类:

class Parent(ABC):
    @classmethod
    @abstractmethod
    def method(cls, *args, basepath: str = None):
        raise NotImplementedError()

和一个具体的子类,例如:

class Child(Parent):
    @classmethod
    def method(cls, arg1: int, arg2: str, basepath: str = None):
        do_stuff_with_args(arg1, arg2)
        do_stuff_with_basepath(basepath)

我的 IDE (PyCharm) 告诉我

Child.method
的方法签名与
Parent.method
不匹配。这似乎是一个不正确的检查,因为子实现是 Liskov 可替代父实现的。也就是说,孩子的具体参数仍然满足父母的 *args 要求。

这个实现的原因是不同的孩子可以有不同的特定参数,但我们强制在每个孩子实现中包含

basepath

显然这对代码的执行来说不是问题,事实上 Liskov 有点不是问题,因为父级无论如何都是 ABC。我试图了解我是否遗漏了什么,或者 PyCharm 是否只是愚蠢。

是PyCharm错了还是我错了?为什么?

python-3.x inheritance pycharm signature liskov-substitution-principle
1个回答
0
投票

这些关于继承的理论问题通常需要更多思考并获得“正确”答案,而不是在实践中使用 OOP 和继承。

就是说,如果你可以忽略 PyCharm 对这个问题的警告,那就去做吧。

我自己甚至不喜欢 LSP 本身的严格执行——因为当你遇到“真实世界”的情况时,你会经常发现你需要父类可用的实例,如果子类可用,而不是相反。或者其他时候,您只想对功能进行分组并实现良好的代码重用,而不管继承层次结构中的任何成员是否可以替代其他成员。

在 Python 中,参数、参数、属性和所有其他允许的动态事物具有灵活性,这变得更加明显。

仍然,您可以尝试做一件事来检查 PyCharm 是否会停止抱怨 - 或者至少,它可以让您更不容易出错:那就是强制

basepath
是子类中的仅命名参数:

    def method(cls, arg1: int, arg2: str, *, basepath: str = None):
        do_stuff_with_args(arg1, arg2)
        do_stuff_with_basepath(basepath)

额外的“*”确保 basepath 不能作为位置参数传递,就像它不能在基类中一样,因为

*args
在那里。这是一个让我自己说这两个签名不兼容的区别。

© www.soinside.com 2019 - 2024. All rights reserved.