简体   繁体   English

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

[英]Python classmethod constructor inheritance with different signature

TLDR; TLDR; I am using a @classmethod as a constructor for my class, and I need to override it with a different signature for one specific child class that needs extra parameters.我正在使用@classmethod作为我的 class 的构造函数,并且我需要为需要额外参数的特定子 class 使用不同的签名覆盖它。 PyCharm gives a warning about overriding a method with different signature. PyCharm 给出了关于覆盖具有不同签名的方法的警告。 I wonder whether it also applies to @classmethod constructors.我想知道它是否也适用于@classmethod构造函数。


I am using the IDE PyCharm for my python project and I have received the following warning regarding the overriding of a method in a class:我正在为我的 python 项目使用 IDE PyCharm 并且我收到以下关于覆盖 ZABB14DC2A2 中的方法的警告:

Signature of method [...] does not match signature of base method in class [...]方法的签名 [...] 与 class [...] 中的基本方法的签名不匹配

I understand this is related to the Liskov substitution principle, meaning objects of a parent class should always be replaceable by objects of a child class.我知道这与 Liskov 替换原则有关,这意味着父 class 的对象应该始终可以被子 class 的对象替换。

However, in my case I am overriding a @classmethod which is used as a constructor, following some sort of factory pattern.但是,在我的情况下,我正在重写用作构造函数的@classmethod ,遵循某种工厂模式。 A simplification of my code would be as follows:我的代码的简化如下:

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, common, data, specific):
        super().__init__(common, data)
        self.specific = specific
    @classmethod
    def from_directory(cls, data_dir, common, specific):
        all_data = [load_data(data_file) for data_file in get_data_files(data_dir)]
        return [cls(common, data, specific) for data in all_data]

In this example, basically I have a parent class Parent with some common attribute that all child classes will inherit, and some particular child class ChildA which has an extra, subclass-specific attribute.在这个例子中,基本上我有一个父 class Parent ,它有一些所有子类都将继承的公共属性,还有一些特定的子 class ChildA ,它有一个额外的子类特定属性。

Since I am using the @classmethod as a constructor, I assume the Liskov principle does not apply, just in the same way that the __init__() method can be overridden with a different signature.由于我使用@classmethod作为构造函数,我假设 Liskov 原则不适用,就像可以用不同的签名覆盖__init__()方法一样。 However, the PyCharm warning has made me consider whether there is something I might have missed.然而,PyCharm 警告让我考虑是否有我可能遗漏的东西。 I am not sure whether I am using the @classmethod in a sensitive way.我不确定我是否以敏感的方式使用@classmethod

My main question is then: Is PyCharm being overzealous with its warnings here or is there any reason the pattern described above should be avoided?那么我的主要问题是: PyCharm 是否过于热衷于此处的警告,或者是否有任何理由应该避免上述模式?

Also, any feedback about any other design issues / misconceptions I might have is most welcome.此外,任何关于我可能有的任何其他设计问题/误解的反馈都是非常受欢迎的。

I would refine your class method.我会改进你的 class 方法。 There are really two class methods to provide here: one that creates an instance of the class from a data file, and one that produces a list of instances from the files in a directory (using the first class method).这里实际上提供了两种class 方法:一种从数据文件创建 class 的实例,另一种从目录中的文件生成实例列表(使用第一个 class 方法)。 Further, the class methods shouldn't care about which arguments cls will need: it just passes on whatever it receives (with the exception of data , which it knows about and will provide or override with whatever it reads from a file).此外, class 方法不应该关心哪个 arguments 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

Notice that you no longer need to override Parent.from_directory ;请注意,您不再需要覆盖Parent.from_directory it's already agnostic about what arguments it receives that are intended for __init__ .它已经不知道它收到的 arguments 是用于__init__的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM