How to call child constructor from parent? - python

In inheritance, most of the time we want to create child classes that inherit from the parent, and in the process of instantiation they have to call the parent constructor. In python we use super for this, and that's great.
I want to do somewhat the opposite: I have a parent class which is a template for a number of child classes. Then I want the child classes to each have a function that allows an instance to clone itself:
class Parent(object):
def __init__(self, ctype, a):
print('This is the parent constructor')
self._ctype = ctype
self._a = a
#property
def a(self):
return self._a
#property
def ctype(self):
return self._ctype
class ChildOne(Parent):
def __init__(self, a):
super(ChildOne, self).__init__('one', a)
print('This is the child One constructor')
self.one = 1
def clone(self):
return ChildOne(self._a)
class ChildTwo(Parent):
def __init__(self, a):
super(ChildTwo, self).__init__('two', a)
print('This is the child Two constructor')
self.two = 2
def clone(self):
return ChildTwo(self._a)
Now, if I create an instance of one of the children, I can clone it:
>>> k = ChildOne(42)
>>> k.ctype
'one'
>>> l = k.clone()
>>> l.a
42
>>> l is k
False
The problem is, the clone method is repeated- and nearly identical- in both sub-classes, except I need to specify explicitly which constructor to call. Is it possible to design a clone method that I define in the parent class, that correctly inherits to the children?

This can be done with:
Code:
class Parent(object):
def clone(self):
return type(self)(self._a)
Test Code:
class Parent(object):
def __init__(self, ctype, a):
print('This is the parent constructor')
self._ctype = ctype
self._a = a
#property
def a(self):
return self._a
#property
def ctype(self):
return self._ctype
def clone(self):
return type(self)(self._a)
class ChildOne(Parent):
def __init__(self, a):
super(ChildOne, self).__init__('one', a)
print('This is the child One constructor')
self.one = 1
class ChildTwo(Parent):
def __init__(self, a):
super(ChildTwo, self).__init__('two', a)
print('This is the child Two constructor')
self.two = 2
k = ChildOne(42)
print(k.ctype)
l = k.clone()
print(l.a)
print(type(l))
Results:
This is the parent constructor
This is the child One constructor
one
This is the parent constructor
This is the child One constructor
42
<class '__main__.ChildOne'>

Related

Can Python subclasses return class of type(self) from method inherited from a parent class?

Suppose I have a parent class Parent and two child classes Child1 and Child2. Suppose Parent contains a method usable by both subclasses. Is it possible for this method to return a new instance of the same class of whatever class calls it, even if the particular subclass calling it is not known when the method is defined in the parent class?
class Parent:
def __init__(self, foo):
pass
def my_method(self):
return self.__class__(foo)
class Child1(Parent):
def __init__(self, foo):
super().__init__(foo)
class Child2(Parent):
def __init__(self, foo):
super().__init__(foo)
parent = Parent('hello')
type(p.my_method())
>>> class<Parent>
child1 = Child1('hey')
type(child1.my_method())
>>> class<Child1>
child2 = Child2('yo')
type(child2.my_method())
>>> class<Child2>
EDIT
I had made a mistake in passing foo without initializing it as an attribute first. The following code achieves the behavior I'm going for, but I'd be interested to know if this could lead to design problems down the road, so feedback on that would be appreciated.
class Parent:
def __init__(self, foo):
self.foo = foo
def my_method(self):
return self.__class__(self.foo)
class Child1(Parent):
def __init__(self, foo):
super().__init__(foo)
class Child2(Parent):
def __init__(self, foo):
super().__init__(foo)
p = Parent(foo='hi')
print(type(p.my_method()))
>>>__main__.Parent
c1 = Child1(foo='hey')
print(type(c1.my_method()))
>>>__main__.Child1
c2 = Child2(foo='hey')
print(type(c2.my_method()))
>>>__main__.Child2
What you have won't work, because you have my_method being an instance method, but you're trying to call it without an instance.
If you make that a class method, it can work, although I personally think this is a bad design choice:
class Parent:
def __init__(self, foo):
pass
#classmethod
def my_method(cls):
return cls(0)
class Child1(Parent):
def __init__(self, foo):
super().__init__(foo)
class Child2(Parent):
def __init__(self, foo):
super().__init__(foo)
print(type(Parent.my_method()))
print(type(Child1.my_method()))
print(type(Child2.my_method()))
Output:
<class '__main__.Parent'>
<class '__main__.Child1'>
<class '__main__.Child2'>

Inheritance - Calling methods within methods

I know the super() function allows you to call a parent method from a child class. So in this example:
class Exam():
def __init__(self, a):
self.a = a
def show(self):
print("This is from parent method")
class Quiz(Exam):
def __init__(self, b, a):
super().__init__(a)
self.b = b
def show(self):
print("This is from child method")
super().show()
q = Quiz('b', 'a')
q.show()
>>> 'This is from child method'
>>> 'This is from parent method'
What if I added another method called get_score to both the parent and child class like here:
class Exam():
def __init__(self, a):
self.a = a
def show(self):
print("This is from parent method")
print (self.get_score())
def get_score(self):
return self.a
class Quiz(Exam):
def __init__(self, b, a):
super().__init__(a)
self.b = b
def show(self):
print("This is from child method")
super().show()
print (self.get_score())
def get_score(self):
return self.b
q = Quiz('b', 'a')
q.show()
>>> 'This is from child method'
>>> 'This is from parent method'
>>> 'b'
>>> 'b'
I can understand why calling super().show() in the child class would return 'b' since I am overwriting the get_score() method.
However, is there a way to maintain the integrity of the parent class so that when I do call super().show()
I get this instead?
>>> 'This is from child method'
>>> 'This is from parent method'
>>> 'a'
>>> 'b'
Sorry in advance if this is bad design. Let me know what other alternatives I can take, even if it means I should the name of the method to avoid this kind of collision.
Use name-mangling, which will prevent the name-collisions in the subclass
:
class Exam():
def __init__(self, a):
self.a = a
def show(self):
print("This is from parent method")
print (self.__get_score())
def __get_score(self):
return self.a
get_score = __get_score
class Quiz(Exam):
def __init__(self, b, a):
super().__init__(a)
self.b = b
def show(self):
print("This is from child method")
super().show()
print (self.__get_score())
def __get_score(self):
return self.b
get_score = __get_score
q = Quiz('b', 'a')
q.show()

Most pythonic way to super parent class and pass class variables

I have a parent class and a child class. The parent class needs some predefined class variables to run call(). The objects are not defined in the child class.
Question: What is the most pythonic way to pass the variables when calling super() without changing the parent class.
Example:
class Parent:
def __init__(self):
self.my_var = 0
def call(self):
return self.my_var + 1
class Child(Parent):
def __init__(self):
self.different_var = 1
def call(self):
my_var = 0
super().__call__() # What is the most pythonic way of performing this line
I know I could just make my_var in the child class a class object and it would work, but there must be a better. If not that would be an acceptable answer as well.
Your version is just a mixin. You have to __init__ the super.
class Parent:
def __init__(self):
self.my_var = 0
def call(self):
return self.my_var + 1
class Child(Parent):
def __init__(self):
super().__init__() #init super
self.different_var = 1
def call(self):
self.my_var = 50
return super().call() #return call() from super
c = Child()
print(c.call()) #51

How to inherit from pre-existing class instance in Python?

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"
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"
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()
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.
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

How to pass all class variables from a parent *instance* to a child class?

Here's an example of what I'm trying to do:
class Parent():
def __init__():
self.parent_var = 'ABCD'
x = Child(self) # self would be passing this parent instance
class Child():
def __init__(<some code to pass parent>):
print(self.parent_var)
foo = Parent()
Now I know what you're thinking, why not just pass parent_var itself to the child instance? Well my actual implementation has over 20 class variables in Parent. I don't want to have to manually pass each variable to the __init__ of the Child instance that's instantiated in Parent-- is there a way to make all Parent class variables available to Child?
EDIT - SOLVED:
This is the way I found that works:
class Parent():
def __init__(self):
self.parent_var = 'ABCD' # but there are 20+ class vars in this class, not just one
x = Child(self) # pass this parent instance to child
class Child():
def __init__(self, parent):
for key, val in vars(parent).items():
setattr(self, key, val)
print(self.parent_var) # successfully prints ABCD
foo = Parent()
If you inherit from the parent class all variables will be present in child classes. Use super init in the child to make sure the parent class instantiates.
class Parent:
def __init__(self):
self.parent_var = 'ABCD'
class Child(Parent):
def __init__(self):
super().__init__()
child = Child()
print(child.parent_var)
prints:
'ABCD'
You would pass the instance of Parent like you would any value.
class Parent:
def __init__(self):
self.parent_var = 'ABCD'
x = Child(self)
class Child:
def __init__(self, obj):
print(obj.parent_var)
Found a solution and wanted to post the answer in case anyone who finds this needs it:
class Parent():
def __init__(self):
self.parent_var = "ABCD" # just an example
x = Child(self) # pass this parent instance (this object) to child
class Child():
def __init__(self, parent):
# copies variables from passed-in object to this object
for key, val in vars(parent).items():
setattr(self, key, val)
print(self.parent_var) # successfully prints ABCD
foo = Parent()

Categories

Resources