Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
4.0k views
in Technique[技术] by (71.8m points)

Python classmethod constructor inheritance with different signature

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. PyCharm gives a warning about overriding a method with different signature. I wonder whether it also applies to @classmethod constructors.


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:

Signature of method [...] does not match signature of base method in 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.

However, in my case I am overriding a @classmethod which is used as a constructor, following some sort of factory pattern. 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.

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. However, the PyCharm warning has made me consider whether there is something I might have missed. I am not sure whether I am using the @classmethod in a sensitive way.

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?

Also, any feedback about any other design issues / misconceptions I might have is most welcome.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I would refine your class method. 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). 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 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; it's already agnostic about what arguments it receives that are intended for __init__.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...