classmethod as constructor and inheritance
Asked Answered
C

2

10

The problem is quite simple. If a class B inherit a class A and wants to override a ´classmethod´ that is used as a constructor (I guess you call that a "factory method"). The problem is that B.classmethod will want to reuse A.classmethod, but then it will have to create an instance of the class A, while it subclasses the class A - since, as a classmethod, it has no self. And then, it doesn't seem the right way to design that.

I did the example trivial, I do more complicate stuff by reading numpy arrays, etc. But I guess there is no loss of information here.

class A:
    def __init__(self, a):
        self.el1 = a

    @classmethod
    def from_csv(cls, csv_file):
        a = read_csv(csv_file) 
        return cls(a)

    @classmethod
    def from_hdf5 ...

class B(A):
    def __init__(self, a, b)
        A.(self, a)
        self.el2 = b

    @classmethod
    def from_csv(cls, csv_file):
        A_ = A.from_csv(csv_file) #instance of A created in B(A)
        b = [a_*2 for a_ in A.el]
        return cls(A.el, b) 

Is there a pythonic way to deal with that?

Cero answered 4/1, 2014 at 18:44 Comment(1)
I have the same problem, and it's surprising that this question doesn't have a satisfying answer. It should!Bags
C
2

After doing some different trials. My conclusion is that you should override a classmethod without reusing the code inside. So the best way I found, for my particular problem, is to make the classmethod as simply as possible and put the code I want to reuse in another method, static in my case, since the classmethod is a constructor.

Cero answered 6/1, 2014 at 2:9 Comment(0)
G
1

One easy solution would be to have class B's __init__ method have a default value for its b parameter. This would let the cls(a) call made by A.from_csv work when it is inherited. If the default is used, the __init__ method could calculate a value to store from a (as you do in B.from_csv now).

class B(A):
    def __init__(self, a, b=None):
        super().__init__(a)   # use super(B, self).__init__(a) if you're in Python 2
        self.el2 = b if b is not None else [i*2 for i in a]

    # don't override from_csv, B.from_csv will already return a B instance!
Groundhog answered 4/1, 2014 at 19:11 Comment(3)
This solution doesn't seem well done for my problem, since, as suggested in the example, I have plenty of factory method, I don't get how to deal with that with your solution. Currently, if I get it well, I'm adopting a close solution. A.from_csv() calls a @staticmethod format_array() that is overloaded.Cero
@Touki: I guess it depends on how your B instances should be constructed in the other factories. If the b parameter should always be derived from the a parameter (e.g. by doubling each value, as in your example), then I think the idea of doing that derivation in the __init__ method will carry over (you don't need to override the A factories, which will construct B instances when they're called with cls equal to B). If the b values need to come from somewhere else, there's not any easy way to inherit, I don't think (unless you factor out more bits of the code).Groundhog
In the second case, isn't it better to abandon @classmethod and to replace it whether (1) by parsing an enormous amount of args in init to let him play the role of multiple constructor or (2) by simply have an empty constructor, and then by calling a method?A.(), A.from_csv()Cero

© 2022 - 2024 — McMap. All rights reserved.