具有不同签名的Python类方法构造函数继承

问题描述

TLDR; 我使用 @classmethod 作为我的类的构造函数,我需要为一个需要额外参数的特定子类使用不同的签名覆盖它。 PyCharm 给出关于覆盖具有不同签名的方法的警告。我想知道它是否也适用于 @classmethod 构造函数


我正在将 IDE PyCharm 用于我的 Python 项目,并且我收到了以下关于重写类中方法的警告:

方法 [...] 的签名与类 [...] 中基方法的签名不匹配

我理解这与 Liskov 替换原则有关,这意味着父类的对象应该始终可以被子类的对象替换。

但是,就我而言,我遵循某种工厂模式覆盖了用作构造函数@classmethod。我的代码的简化如下:

class Parent:
    def __init__(self,common,data):
        self.common = common
        self.data = data
    @classmethod
    def from_directory(cls,data_dir,common):
        all_data = [load_data(data_file) for data_file in get_data_files(data_dir)]
        return [cls(common,data) for data in all_data]

class ChildA(Parent):
    def __init__(self,data,specific):
        super().__init__(common,data)
        self.specific = specific
    @classmethod
    def from_directory(cls,specific):
        all_data = [load_data(data_file) for data_file in get_data_files(data_dir)]
        return [cls(common,specific) for data in all_data]

在这个例子中,基本上我有一个父类 Parent,它有一些所有子类都将继承的公共属性,还有一些特定的子类 ChildA,它有一个额外的、特定于子类的属性

因为我使用 @classmethod 作为构造函数,所以我假设 Liskov 原则不适用,就像 __init__() 方法可以用不同的签名覆盖一样。然而,PyCharm 的警告让我考虑是否有我可能遗漏的东西。我不确定我是否以敏感的方式使用 @classmethod

我的主要问题是: PyCharm 是否对这里的警告过于热情,或者是否有任何理由应该避免上述模式?

此外,非常欢迎对我可能遇到的任何其他设计问题/误解的任何反馈。

解决方法

我会改进你的课堂方法。这里确实提供了两种类方法:一种从数据文件创建类的实例,另一种从目录中的文件生成实例列表(使用第一种类方法)。此外,类方法不应该关心 cls 需要哪些参数:它只传递它接收到的任何内容(data 除外,它知道并将提供或覆盖它的任何内容)从文件中读取)。

class Parent:
    def __init__(self,common,data,**kwargs):
        super().__init__(**kwargs)
        self.common = common
        self.data = data

    @classmethod
    def from_file(cls,filename,**kwargs):
        # If the caller provided a data argument,# ignore it and use the data from the file instead.
        kwargs['data'] = load_data(filename)
        return cls(**kwargs)

    @classmethod
    def from_directory(cls,data_dir,**kwargs):
        return [cls.from_file(data_file,**kwargs)
                for data_file in get_data_files(data_dir)]
        

class ChildA(Parent):
    def __init__(self,specific,**kwargs):
        super().__init__(**kwargs)
        self.specific = specific

请注意,您不再需要覆盖 Parent.from_directory;它已经不知道它接收到哪些用于 __init__ 的参数。