ProfHase85
ProfHase85

Reputation: 12253

initialize child class with parent

Having a parent and a child class I would like to initialize the child class with a parent instance. My way seems very cumbersome (see below):

I define a staticmethod to extract init parameters for parent initialization:

class Parent(object):
    @staticmethod
    get_init_params(parent_obj):
        a = parent_obj.a
        b = parent_obj.b
        return (a, b)

    def __init__(self, a, b):
        self.a = a
        self.b = b

class Child(Parent):
    def __init__(self, parent):
        super(Parent, self).__init__(*get_init_params(parent))

Is there possibly a more direct way?

EDIT now the classes are simpler

Upvotes: 10

Views: 31016

Answers (4)

jwilner
jwilner

Reputation: 6606

Make your parent (and hence child) constructor take changed a and b. You should have as little behavior in init methods as possible. Create a class method that takes unchanged a and b, changes them, and then applies the class constructor to them -- this would be good for both parent and child. Then write a static method on the child class that takes a parent object and passes its changed a and b attributes to the init method that expects changed a and b.

class Parent(object):
    @classmethod 
    def from_unchanged(cls, a, b):
        changed_a = change_a(a) 
        changed_b = change_b(b) 
        return cls(a, b) 

    @classmethod
    def from_parent(cls, parent):
        return cls(parentchanged_a, parentchanged_b) 

    def __init__(self, changed_a, changed_b) :
        selfchanged_a = changed_a
        selfchanged_b = changed_b

Upvotes: -1

chepner
chepner

Reputation: 532418

Based on your comments in my original answer, I believe you don't want to subclass Parent; rather, you just want alternate constructors.

class Parent(object):

    # The basic initializer; just use whatever values are
    # passed directly to it
    def __init__(self, a, b):
        self.a = a
        self.b = b

    @classmethod
    def type_1(cls, a, b):
        return cls(change_a1(a), change_b1(b))

    @classmethod
    def type_2(cls, a, b):
        return cls(change_a2(a), change_b2(b))

    # etc

If you really do want a subclass of Parent, then there's no need to back out any changes; Parent only includes functionality that applies to all children.

class Child(Parent):
    def __init__(self, a, b):
        super(Child, self).__init__(a, b)
        # Any Child-specific initialization

Upvotes: 1

chepner
chepner

Reputation: 532418

I think you want to separate the notion of intializing a Child object from the notion of creating one from a Parent. The get_init_params is just adding a layer of complexity you don't need; access the attributes directly.

class Child(Parent):
    @classmethod
    def from_parent(cls, parent):
        return cls(parent.a, parent.b)

    def __init__(self, a, b):
        super(Child, self).__init__(a, b)
        # Note: the fact that yo have to do this,
        # or not call the parent's __init__ in the first
        # place, makes me question whether inheritance
        # is the right tool here.
        self.a = revert_change(self.a)
        self.b = revert_change(self.b) 

p = Parent(3, 5)
c1 = Child.from_parent(p)
c2 = Child(6, 6)

If there are changes to make to the values you get from the parent, apply them in to_parent before creating the Child object.

def from_parent(cls, parent):
    return cls(revert_change(parent.a), revert_change(parent.b))
    # Or, if you save the original values
    # return cls(parent.orig_a, parent.orig_b)

Upvotes: 12

Adam Smith
Adam Smith

Reputation: 54273

Why not save the init parameters and create a new bound method on Parent to make_child()?

class Parent(object):
    def __init__(self, *args, **kwargs):
        self.init_args = {'args':args, 'kwargs':kwargs}
        self.children = list()
        ...  # whatever else a Parent does

    def make_child(self, child_cls, *args, **kwargs):
        if args is None:
            args = self.init_args['args']
        if kwargs is None:
            kwargs = self.init_args['kwargs']
        child = child_cls(self, *args, **kwargs)
        self.children.append(child)
        return child


class Child(Parent):
    def __init__(self, parent, *args, **kwargs):
        self.parent = parent
        ...  # whatever else a Child does

 DEMO

>>> p = Parent()
>>> c = p.make_child(Child)
>>> c in p.children
True

Upvotes: 0

Related Questions