Python overriden methods called from superclass - python

There are lot of information about mangling and it's usage, however I am struggling to understand the following piece of code:
class Parent:
NAME = 'PARENT'
def __init__(self):
self.print_me()
def print_me(self):
print(f"Parent class {self.NAME}")
class Child(Parent):
NAME = 'CHILD'
def __init__(self):
super().__init__()
def print_me(self):
print(f"Child class {self.NAME}")
c = Child()
Can someone please explain how the overriden method (print_me) is called from the parent class init and does not print Parent class PARENT?
If I use mangling both for NAME and print_me the method is not overriden and thus it's called from the parent which is expected.

Here the object(c) is child class object
In child class the constructor calls the parent class and the parent class constructor calls the print_me method
Here you called the child object so it will execute the child method. If the print_me method is not available in child then it will execute parent method

If you needed it to print the parent function, you should instead create the parent object.
c = Parent()

Related

Inheritance: How to make parent method work with other parent methods?

So I have two classes, where one inherits from another and overrides all parent methods:
class Parent:
def name(self):
return 'parent'
def run(self):
print(f'calling method from parent: I am {self.name()}')
class Child(Parent):
def name(self):
return 'child'
def run(self):
print(f'calling method from child: I am {self.name()}')
return super().run()
Running the following piece Child().run() triggers method run both for child and parent, and the output is:
calling method from child: I am child
calling method from parent: I am child
And result is clear - since we have redefined method name, new version is used in both run methods. (I am child on both lines)
And that's the main problem - the way I want it to work is for parent run method to use parent name.
What I have accomplished so far is replacing super with self.__class__.__mro__[1], so that method looks like
def run(self):
print(f'calling method from child: I am {self.name()}')
return self.__class__.__mro__[1]().run()
The way it works is it gets parent class using method resolution order and creates instance of parent class. It works fine and now result is:
calling method from child: I am child
calling method from parent: I am parent
But I don't like this solution:
Single inheritance - since we hardcode parent class index, we cannot make it work with several parent classes
Using MRO doesn't feel right for this case
Here we assume __init__ doesn't take extra arguments
I think clue is in changing self.name in parent method so that it will use parent method explicitly, but I don't know how to achieve this.
You can find the current class that the method is defined in by using __class__:
class Parent:
def name(self):
return 'parent'
def run(self):
print(f'calling method from parent: I am {__class__.name(self)}')
I'd suggest making name a name-mangled (__) attribute that's private to the class:
class Parent:
__name = 'parent'
def run(self):
print(f'calling method from parent: I am {self.__name}')
class Child(Parent):
__name = 'child'
def run(self):
print(f'calling method from child: I am {self.__name}')
return super().run()
Child().run()
# calling method from child: I am child
# calling method from parent: I am parent

Create child class instances from parent class instance, and call parent methods from child class instance

I wish I could do 2 things:
Create as many times of the Child class from an instance of the Parent class (What I can do)
Call a method of the Parent class from an instance of the Child class (What I can't do)
To illustrate my problem, I created 2 instances of the class Parent to which I add an instance of the class Child to each.
from datetime import datetime, timedelta
class Child:
def __init__(self):
pass
def ask_time(self): # This function doesn't work,
return self.read_time() # But I would like to be able to call here the "read_time()" method of the class "Parent"
class Parent:
def __init__(self, name, minutes_fast):
self.name = name
self.minutes_fast = minutes_fast
self.children = {}
def add_child(self, name): # Construct "Child" class instance from class "Parent" class instance
self.children[name] = Child() # Because of this line, I cannot inherit "class Child (Parent):"
def get_child(self, name):
if name not in self.children:
self.add_child(name)
return self.children[name]
def read_time(self):
current_time = datetime.now()
delta = timedelta(minutes=self.minutes_fast)
return (current_time + delta).strftime("%H:%M:%S")
# Add the Parent "James" who is 3 minutes early to his watch, and add him the child "John"
parent1 = Parent("James", 3)
child1 = parent1.get_child("John")
# Add the Parent "Michael" who is 1 minutes early to his watch, and add him the child "Matthew"
parent2 = Parent("Michael", 1)
child2 = parent2.get_child("Matthew")
print(parent1.read_time())
print(parent2.read_time())
In my use case, reading the time is the responsibility of the class Parent. So I added the read_time() method to this one.
But an instance of the class Child must be able to request the time from the instance of the class Parent that created it. So I add the ask_time() method to the class Child which calls the read_time() method of the class Parent... Which does not work without inheriting between my classes (from the following way class Child(Parent):).
Which would allow me to do this, and what I now need to do.
print(child1.ask_time())
print(child2.ask_time())
But I don't see how to inherit when class Parent itself depends on the class Child?
Thanks for your help !
You have confused functional dependency with class inheritance. You do not have to inherit read_time from Parent. In fact, your implementation shows that this is not sufficient. As you correctly designed this, read_time is an instance attribute: it makes no sense to call read_time without specifying which Parent instance should respond.
You need to give each child a reference to its parent. Include this in add_child:
def add_child(self, name):
baby = Child(self, name)
self.children[name] = baby
Change the Child initialization to use the appropraite info:
def __init__(self, parent, name):
self.parent = parent
self.name = name
It seems silly to have the child's name be a property only of the parent's list.
Now, when the child needs to ask the time, we have:
def ask_time(self):
return self.parent.read_time()

Prevent a method that is called in the parent constructor from being called in the child constructor

Suppose I have a parent class and a child class that inherits from the parent.
class Parent:
def __init__(self)
stubborn()
class Child():
def __init__(self):
super().__init__(self)
I do not want the stubborn method to be called anytime I call the parent constructor
in the child class. How do I approach this?
You can define a classmethod of Parent that checks whether or not you are in Parent, then use that to determine whether to call stubborn
class Parent:
def __init__(self):
if self.is_parent():
self.stubborn()
#classmethod
def is_parent(cls):
return cls is Parent
def stubborn(self):
print("stubborn called")
class Child(Parent): pass
p = Parent() # stubborn called
c = Child() # no output
You wouldn't be able to do anything about it in parent.__init__() without actually changing that function or stubborn().
But, as the child, you could stop the stubborn() method from doing anything important by temporarily making it a stub:
class Child():
def __init__(self):
old_stubborn = self.stubborn
self.stubborn = lambda:None # stub function that does nothing
super().__init__(self)
# put stubborn() back to normal
self.stubborn = old_stubborn

Does a subclass need to initialize an empty super class?

This just came into my mind.
class Parent:
pass
class Child(Parent):
def __init__(self):
# is this necessary?
super().__init__()
When a class inherits an empty class, do the subclass need to initialize it and why?
This is just fine:
class Parent:
# the __init__ is inherited from parent
pass
class Child(Parent):
# the __init__ is inherited from parent
pass
This is also fine:
class Parent:
# the __init__ is inherited from parent
pass
class Child(Parent):
def __init__(self):
# __init__ is called on parent
super().__init__()
This may seem ok, and will usually work fine, but not always:
class Parent:
# the __init__ is inherited from parent
pass
class Child(Parent):
def __init__(self):
# this does not call parent's __init__,
pass
Here is one example where it goes wrong:
class Parent2:
def __init__(self):
super().__init__()
print('Parent2 initialized')
class Child2(Child, Parent2):
pass
# you'd expect this to call Parent2.__init__, but it won't:
Child2()
This is because the MRO of Child2 is: Child2 -> Child -> Parent -> Parent2 -> object.
Child2.__init__ is inherited from Child and that one does not call Parent2.__init__, because of the missing call to super().__init__.
No it isn't necessary. It is necessary when you want the parent's logic to run as well.
class Parent:
def __init__(self):
self.some_field = 'value'
class Child(Parent):
def __init__(self):
self.other_field = 'other_value'
super().__init__()
child = Child()
child.some_field # 'value'
There is no requirement in the language that subclass need to call __init__ of superclass. Despite this, it is almost always needed because superclass initializes some base attributes and the subclass expects them to be initialized. So, if the superclass __init__ is empty, you don't need to call it, otherwise you need to.

Turn a instance to its class's child type (calling super for classmethod)

class Parent:
def __init__(self, name):
self.name = name
#classmethod
def get(cls, name):
return cls(name)
def greeting(self):
print 'Hello ' + self.name
class Child(Parent):
#classmethod
def get(cls, name):
p = Parent.get(name)
p.full_name = 'James ' + name
return p
def greeting(self):
print 'Bye ' + self.full_name
as you can see, I added an attribute full_name in the Child's get method to the instance of Parent, the problem is, now the instance if still in the type of Parent, how could I turn it into Child?
You don't need to change the type of the returned object. What you need to do is call the parent class's implementation of the method with the same cls the child class's method is using. This is one of the things super is for. First, you'll need to make sure you're using new-style classes:
class Parent(object):
# continue as usual
Python has two implementations of the class system, because they didn't get it right the first time. Always inherit from object if you're not inheriting from anything else, to make sure you're using the newer, better system.
Now, super. Instead of using Parent.get(name), use a super call:
p = super(Child, cls).get(name)
p will have the correct type, since cls in the parent's method was the same class as cls in the child's method. (Note that if you later added a Grandchild class, this line would produce Grandchild objects instead of Child objects. This is most likely the right behavior.)
Make object as Parent base class:
class Parent(object):
and then change this line in Child p = Parent.get(name) to:
p = super(Child, cls).get(name)
This way super(Child, cls).get(name) will bound cls (Child) to parent get method, so in result Parent.get will be called with Child class as cls parameter. Of course to make this work you need new style classes that why object should be in Parent bases!

Categories