0

This is a corner I have painted myself into a few times. I would have thought given how common alternate constructors are in Python that it would not be unheard of to construct a subclass from an alternate constructor of its superclass, but I can't think of how to do that or find a discussion about it online.

To illustrate the problem. Say I have the following toy superclass.

class Parent:

    def __init__(self):
        self.x = 0

    @classmethod
    def from_x(cls, x):
        a = cls()
        a.x = x
        return a

Now say that I want to subclass Parent using its from_x method. I can think of four unsatisfying solutions.

  1. Calling the superclass's alternate constructor in the subclass's __init__ as shown below fails because from_x does not modify its input like __init__ but returns a value more like __new__.

    # failed solution
    
    class Child(Parent):
    
        def __init__(self):
            super().from_x(1)
    
  2. Calling the superclass's from_x method in the subclass's __new__ as shown below also fails because both methods call each other circularly.

    # failed solution
    
    class Child(Parent):
    
        def __new__(self):
            return super().from_x(1)
    
  3. Overriding the superclass's from_x method in the subclass makes the subclass's important __init__ method vestigial and makes it assign the wrong value to x.

    # bad solution
    
    class Child(Parent):
    
        @classmethod
        def from_x(cls):
            return super().from_x(1)
    
  4. Giving up on inheritance and using composition instead works, but adds an unhelpful layer of syntax (i. e. child.parent.x).

    # unfortunate compromise solution            
    
    class Child:
    
        def __init__(self):
            self.parent = Parent.from_x(3)
    

Did I miss a better option for this goal?

Victor
  • 127
  • 8
  • 1
    If it helps, you can change the class of an instance by setting the `__class__` property. I can't seem to get it to work right in this case, but I think that your `from_x` method should be able to create a parent and then set `a.__class__` to `cls` before returning it. – Matthew Jan 14 '16 at 00:13
  • @jonrsharpe, thank you for the link to the duplicate question. I was unable to find that myself. Your answer there offers the equivalent of my third solution to this problem, which disappoints me because it makes the subclass's important `__init__` method, at best, secondary to `from_x` and, at worst, a practically guaranteed error that complicates duck-typing that equivocates between the superclass and subclass. @Mathew, thank you for your suggestion. I also understand your suggestion as going with my third solution. That seems like the concensus on this issue. Thanks. – Victor Jan 14 '16 at 02:10
  • @Matthew, I figured out how to apply your suggestion to get an instance of the superclass then change its `__class__` property to the subclass. It actually works with my second solution above when `super()` is explicitly replaced by the superclass. I submitted this whole solution as an answer to the question that mine duplicates. Thank you again for the suggestion. – Victor Jan 14 '16 at 04:37
  • You did basically what I had intended. I actually had the child new method calling `super.from_x(1)` and had the parent from_x method doing `a = Parent()` and finishing with `a.__class__ = cls` before returning. You are just changing the class on the child instead of the parent like I did. You figured out what was messing me up, though. I hadn't thought of passing in the child init method. My child kept having the x value as 0. It wasn't striking me that the init method was resetting it after it was created. – Matthew Jan 14 '16 at 05:28
  • Could you replace the toy with something more representative of your *actual* situation? You're having this trouble because what you're doing is a bit weird; a sensible alternate constructor class method ends with `return cls(...)`. – jonrsharpe Jan 14 '16 at 07:59

0 Answers0