5

I have a class Parent:

class Parent:
    def __init__(self, foo):
        self.foo = foo

I then have another class Child which extends Parent. But I want Child to take a pre-existing instance of parent and use this as the parent to inherit from (instead of creating a new instance of Parent with the same constructor parameters).

class Child(Parent):
    def __init__(self, parent_instance):
        """ Do something with parent_instance to set this as the parent instance """

    def get_foo(self):
        return self.foo

Then I would ideally be able to do:

p = Parent("bar")
c = Child(p)

print(c.get_foo()) # prints "bar"
  • `self.parent = parent_instance` and then `self.parent.foo` or something similar? – Klaus D. Jun 28 '20 at 16:47
  • @KlausD. Could do, but would prefer proper inheritance, if possible. At the moment your suggestion is what I am doing. – Jony Pollon Jun 28 '20 at 16:49
  • This is a duplicate of the ancient question: https://stackoverflow.com/questions/1081253/inheriting-from-instance-in-python The suggestions still make sense :) – Igor Rivin Jun 28 '20 at 16:54
  • In your example `"bar"` can't get anything else than an instance attribute. But you can only inherit class attributes. – Klaus D. Jun 28 '20 at 16:56

4 Answers4

7

You could copy the content of the parents's __dict__ to the child's. You can use vars() builtin function to do so, and the dictionary's update() method.

class Child(Parent):
    def __init__(self, parent_instance):
        vars(self).update(vars(parent_instance))

    def get_foo(self):
        return self.foo


p = Parent("bar")
c = Child(p)

print(c.get_foo())
# prints "bar"
revliscano
  • 2,227
  • 2
  • 12
  • 21
3

You can use your own constructor - provide a classmethod that takes an instance of a parent.

class Parent:
    def __init__(self, foo):
        self.foo = foo

class Child(Parent):
    def get_foo(self):
        return self.foo

    @classmethod
    def from_parent(cls, parent_instance):
        return cls(parent_instance.foo)


p = Parent('bar')
c = Child.from_parent(p)
c.get_foo()
bbayles
  • 4,389
  • 1
  • 26
  • 34
  • But this will create a new instance of `Parent`, right? I do not want to re-instantiate. – Jony Pollon Jun 28 '20 at 21:29
  • If foo attribute is changed in the parent class, the link will be break in child. (child will keep the older reference) e.g : p = Parent("foo"); c = Child.from_parent(p); p.foo = "spam"; assert c.foo == "foo" – loutre Jun 29 '20 at 10:06
0

Using getattr() to fetch the attribute from the parent instance

class Parent: 
    def __init__(self, foo): 
        self.foo = foo 

class Child(Parent): 
    def __init__(self, parent_instance): 
        self.parent_instance = parent_instance
    
    def get_foo(self): 
        return self.foo 
        
    def __getattr__(self, attr):
        return getattr(self.parent_instance, attr)

par = Parent("bar") 
ch = Child(par)
print(ch.get_foo())
#prints bar
C0l0red
  • 35
  • 1
  • 7
  • I think this will create a new instance of `Parent` though - I want to use the already existing instance of `Parent` that has been passed in. – Jony Pollon Jun 28 '20 at 21:31
  • This will crash if you attach another attribute in parent instance that is not in the init signature. Or if instance attribute is renamed, e.g `self._foo = foo` – loutre Jun 29 '20 at 10:03
  • I've updated my code to work without creating another instance – C0l0red Jul 08 '20 at 18:13
0

I'm not sure inheritance is the right solution here as it breaks the LSP in the __init__ method.

Maybe parents and children just share a common interface. I'd prefer something like (python3.8):

from typing import Protocol
    
class FoeAware(Protocol):
    @property
    def foe(self):
        ...
 
class Parent:
    def __init__(self, foe):
        self._foe = foe
   
    @property
    def foe(self):
        return self._foe

class Child:
    def __init__(self, parent: FoeAware):
        self.parent = parent

    @property
    def foe(self):
        return self.parent.foe

p = Parent("bar")
c = Child(p)
c.foe  # bar

The key point is that it takes advantage of polymorphism with a common interface FoeAware, which is preferable to an inheritance tree.

loutre
  • 874
  • 8
  • 16