Inheritance and Overriding __init__ in python - python

I was reading 'Dive Into Python' and in the chapter on classes it gives this example:
class FileInfo(UserDict):
"store file metadata"
def __init__(self, filename=None):
UserDict.__init__(self)
self["name"] = filename
The author then says that if you want to override the __init__ method, you must explicitly call the parent __init__ with the correct parameters.
What if that FileInfo class had more than one ancestor class?
Do I have to explicitly call all of the ancestor classes' __init__ methods?
Also, do I have to do this to any other method I want to override?

The book is a bit dated with respect to subclass-superclass calling. It's also a little dated with respect to subclassing built-in classes.
It looks like this nowadays:
class FileInfo(dict):
"""store file metadata"""
def __init__(self, filename=None):
super(FileInfo, self).__init__()
self["name"] = filename
Note the following:
We can directly subclass built-in classes, like dict, list, tuple, etc.
The super function handles tracking down this class's superclasses and calling functions in them appropriately.

In each class that you need to inherit from, you can run a loop of each class that needs init'd upon initiation of the child class...an example that can copied might be better understood...
class Female_Grandparent:
def __init__(self):
self.grandma_name = 'Grandma'
class Male_Grandparent:
def __init__(self):
self.grandpa_name = 'Grandpa'
class Parent(Female_Grandparent, Male_Grandparent):
def __init__(self):
Female_Grandparent.__init__(self)
Male_Grandparent.__init__(self)
self.parent_name = 'Parent Class'
class Child(Parent):
def __init__(self):
Parent.__init__(self)
#---------------------------------------------------------------------------------------#
for cls in Parent.__bases__: # This block grabs the classes of the child
cls.__init__(self) # class (which is named 'Parent' in this case),
# and iterates through them, initiating each one.
# The result is that each parent, of each child,
# is automatically handled upon initiation of the
# dependent class. WOOT WOOT! :D
#---------------------------------------------------------------------------------------#
g = Female_Grandparent()
print g.grandma_name
p = Parent()
print p.grandma_name
child = Child()
print child.grandma_name

You don't really have to call the __init__ methods of the base class(es), but you usually want to do it because the base classes will do some important initializations there that are needed for rest of the classes methods to work.
For other methods it depends on your intentions. If you just want to add something to the base classes behavior you will want to call the base classes method additionally to your own code. If you want to fundamentally change the behavior, you might not call the base class' method and implement all the functionality directly in the derived class.

If the FileInfo class has more than one ancestor class then you should definitely call all of their __init__() functions. You should also do the same for the __del__() function, which is a destructor.

Yes, you must call __init__ for each parent class. The same goes for functions, if you are overriding a function that exists in both parents.

Related

In Python how to call Parent class function as if I were Parent object

I have a Parent and a Child class, both should execute their own fct in init but Child have to execute first the Parent fct :
class Parent(object):
def __init__(self):
self.fct()
def fct(self):
# do some basic stuff
class Child(Parent):
def __init__(self):
super().__init__()
self.fct()
def fct(self):
# add other stuff
Problem is that super().init() calls the Child fct and not the Parent one as I would like. Of course I could rename Child function as fct2 but I was wondering if I can do what I want to do without changing names (because fct and fct2 do the same thing conceptually speaking, they just apply on different things). It would be nice if I could call super().__init() as if were Parent object.
The idea of subclassining is this: if you ever need to use a method to the parent class, just do not create it in the child class.
Otherwise, in a hierarchy with complicated classes and mixins, and you really need the methods to have the same name, there is the name mangling mechanism, triggered by Python when using two underlines __ as a method or attribute prefix:
class Parent(object):
def __init__(self):
self.__fct()
def __fct(self):
# do some basic stuff
class Child(Parent):
def __init__(self):
super().__init__()
self.__fct()
def __fct(self):
# add other stuff
Using the __ prefix makes Python change the name of this method, both in declaration and where it is used when the class is created (at the time the class statement with its block is itself executed) - and both methods work as if they were named differently, each one only accessible, in an ordinary way, from code in its own class.
Some documentation, mainly older docs, will sometimes refer to this as the mechanism in Python to create "private methods". It is not the samething, although it can serve the same purpose in some use cases (like this). The __fct method above will be renamed respectively to Parent._Parent__fct and Child._Child__fct when the code is executed.
second way, without name mangling:
Without resorting to this name mangling mechanism, it is possible to retrieve attributes from the class where a piece of code is declared by using the __class__ special name (not self.__class__, just __class__) - it is part of the same mechanism Python uses to make argumentless super() work:
class Parent(object):
def __init__(self):
__class__.fct(self) # <- retrieved from the current class (Parent)
def fct(self):
# do some basic stuff
class Child(Parent):
def __init__(self):
super().__init__()
__class__.fct(self)
def fct(self):
# add other stuff
This will also work - just note that as the methods are retrieved from the class object, not from an instance, the instance have to be explicitly passed as an argument when calling the methods.
The name __class__ is inserted automatically in any methods that use it, and will always refer to the class that has the body were it appears - the class itself will be created "in the future", when all the class body is processed and the class command itself is resolved.

what is the difference between super and inheritance by passing it alone as an object?

in the below example I want to know when I should use one of them for inherits? I think both are valid so, why sometimes I have to use super if the other way is workable to work with?
class User:
def __init__(self):
self._user = "User A"
pass
class UserA(User):
_user = "User B"
def __init__(self):
super().__init__()
class UserB(User):
pass
You are correct, both are valid. The difference is:
UserA: you are overwriting the __init__ method of the ancestor. This is practical if you want to add something during the initialization process. However, you still want to initialize the ancestor, and this can be done via super().__init__(), despite having overwritten the __init__ method.
UserB: you are fully using the __init__ of the ancestor you are inheriting from (by not overwriting the __init__ method). This can be used if nothing extra needs to be done during initialization.
The super() builtin returns a proxy object (temporary object of the superclass) that allows us to access methods of the base class. For example:
class Mammal(object):
def __init__(self, mammalName):
print(mammalName, 'is a warm-blooded animal.')
class Dog(Mammal):
def __init__(self):
print('Dog has four legs.')
super().__init__('Dog')
self represents the instance of the class. By using the “self” keyword we can access the attributes and methods of the class in python

Skip inheritance order call in odoo

I have a class inherited from project.task named ProjectTask
The class has a copy method that overrides the copy function from project.task it's named Task
I need to run the base copy function from my class instead of the one of the parents class
this is my class code:
#api.multi
#api.returns('self', lambda value: value.id)
def copy(self, default=None):
if default is None:
default = {}
if not default.get('name'):
default['name'] = self.name.id
return super(ProjectTask, self).copy(default) #<-- I don't want to call the inherited class method I want to call the base class method instead
This is the copy method from the base class (Task)
#api.multi
#api.returns('self', lambda value: value.id)
def copy(self, default=None):
if default is None:
default = {}
if not default.get('name'):
default['name'] = _("%s (copy)") % self.name
return super(Task, self).copy(default) # <-- I want to run this method from my class (ProjectTask) which is the child class
Any advice will be more than welcome
With the parent class implementation you show, calling it with your own default should do what you want, as it will just pass it through to its own parent with no changes. (At least, that's true with the bare method code, I don't know what the odoo decorators do to change things.)
But if you really do need to skip over it for some non-obvious reason, you probably can do it. Generally speaking, these approaches will only work as intended if you don't expect your class to ever be used with multiple inheritance. If your MRO gets complicated, then you really want to be doing the normal thing with super and making all your methods play nicely together.
One option for skipping an inherited method is to directly name the class you want your call to go to (i.e. your grandparent class).
class Base():
def foo(self):
print("Base")
class Parent(Base):
def foo(self):
print("Parent")
super().foo() # super() in Python 3 is equivalent to super(Parent, self)
class Child(Parent):
def foo(self):
print("Child")
Base.foo(self) # call Base.foo directly, we need to pass the self argument ourselves
Another option would be to change the argument you give to super to name the parent class instead of your own class. Usually that's a newbie error, but if that's really what you want, it's allowed (though I'd strongly recommend adding a comment to the code explaining that you really do want that behavior!
class Child(Parent):
def foo(self):
print("Child")
super(Parent, self).foo() # Note: Deliberately skipping over Parent.foo here!
A final note: If you find yourself wanting to skip a parent class's implementation of some of its methods, perhaps you should reconsider if you should really be inheriting from it at all. It may be that you really want to be inheriting from the same base class as it instead, and skipping the middle class altogether. Obviously, this has its own limitations (maybe some library code does type checking for that class), but if you find yourself fighting the inheritance machinery, it may be that you're doing things the hard way, and there's an easier alternative.

Use of super() function in python 3.6 [duplicate]

I have the following base class and subclass:
class Event:
def __init__(self, sr1=None, foobar=None):
self.sr1 = sr1
self.foobar = foobar
self.state = STATE_NON_EVENT
# Event class wrappers to provide syntatic sugar
class TypeTwoEvent(Event):
def __init__(self, level=None):
self.sr1 = level
self.state = STATE_EVENT_TWO
Further on in my code, I am inspecting an instance of a TypeTwoEvent class, checking for a field I know exists in the base class - I expected it to be defaulted to value None. However, my code raises the following exception:
AttributeError: 'TypeTwoEvent' object has no attribute 'foobar'
I was under the impression that the base class fields would be inherited by the subclass and that creating an instance of a subclass will instantiate the base class (and thus invoke its constructor) ...
What am I missing here? Why does TypeTwoEvent not have a foobar attribute - when the base class from which it is derived has a foobar attribute?
Your subclass should be:
class TypeTwoEvent(Event):
def __init__(self, level=None, *args, **kwargs):
super().__init__(*args, **kwargs)
self.sr1 = level
self.state = STATE_EVENT_TWO
Because you override the __init__ method, so you need to call the parent method if you want the parent behavior to happen.
Remember, __init__ is not a special method dispite its strange name. It's just the method automatically called after the object is created. Otherwise it's an ordinary method, and ordinary inheritance rules apply.
super().__init__(arguments, that, goes, to, parents)
is the syntax to call the parent version of the method.
For *args and **kwargs, it just ensures we catch all additional arguments passed to __init__ and pass it to the parent method, as you child method signature didn't do it and the parent need these arguments to work.
You're overriding the constructor (__init__) of the parent class. To extend it, you need to explicitly call the constructor of the parent with a super() call.
class TypeTwoEvent(Event):
def __init__(self, level=None, **kwargs):
# the super call to set the attributes in the parent class
super().__init__(**kwargs)
# now, extend other attributes
self.sr1 = level
self.state = STATE_EVENT_TWO
Note that the super call is not always at the top of the __init__ method in your sub-class. Its location depends on your situation and logic.
When the instance is created, its __init__ method is called. In this case, that is TypeTwoEvent.__init__. Superclass methods will not be called automatically because that would be immensely confusing.
You should call Event.__init__(self, ...) from TypeTwoEvent.__init__ (or use super, but if you're not familiar with it, read up on it first so you know what you're doing).
You need to call the __init__ method of the base class from the __init__ method of the inherited class.
See here for how to do this.
I've had the same problem, but in my case I put super().__init__() on the bottom of my derived class and that's why it doesn't work. Because I tried to use attributes that are not initialized.

Python Inheritance: Is it necessary to explicitly call the parents constructor and destructor?

I have some Code (for ev3dev):
class Motor(object):
def __init__(self, portName):
self.base = "/sys/class/tacho-motor/motor"
self.number = self.getMotorNumberWithSpecificPortName(portName)
self.name = self.base + str(self.number) + "/"
self.setDefaultValues()
def __del__(self):
self.callReset()
(...)
class TurnMotor(Motor):
def __init__(self):
super(TurnMotor, self).__init__("outA")
def __del__(self):
super(TurnMotor, self).__del__()
The goal is to define multiple motor classes like TurnMotor in this example who inherit from Motor and automatical __init__ with their specific port. They also should call the parents __del__ method on destruction to reset the motor.
I know that in this case I have to define a __init__ method for the subclass to initiate with the port I want but would the parents __del__ method still be called from the subclass if I leave out the definition for __del__ in the subclass?
Would this in general be possible for __init__ too?
Yes it is possible. If you do not overwrite a method within subclass the parent method will be invoked instead. This is also true for magic methods like __del__ or __init__.
Here a small example I run in python cli
>>> class A():
... def __del__(self):
... print('A.__del__')
...
>>> class B(A): pass
...
>>> b = B()
>>> del b
A.__del__
Class B will contain all information of class A mixed with its specific information.
__init__ and __del__ are merely construction and destruction hooks, although this statement can be subject to discussion.
What is important for the programmer is that you do not have to define the super-class con/desctructor. If you don't, Python looks for a con/destructor in the internal lookup chain, i.e. in the base class(es), according to the method resolution order (MRO).
If you want a con/destructor in the derived class and if you want to create or destroy instance variables of the super class (and most of the time you want to do that, as that is why you derived in the first place) or do whatever is done in the super classes con/destructor, then you will have to call the super classes methods accordingly.
This is not done explicitly because (a) Python gives you the chance of not doing it and (b) Python Zen says: "Explicit is better than implicit".

Categories

Resources