Inheritance¶
Inheritance allows to specify a class without rewrite everything.
Imagine you write a 3D modeler program, you are going to need points, cubes, sphere, rectangles and many other shape. Each shape should be an object and all shapes should have properties like a position, a dimension and a name. Therefore it can be useful to define a class with all these common properties :
class Shape:
def __init__(self, dimension, position = [0,0], name = None):
self.dimension = dimension
self.position = position
if dimension == 3 and len(position) == 2: # a correction to incomplete data
self.position.append(0)
self.name = name
class Rectangle(Shape):
def __init__(self, width, length):
super().__init__(2, name = "Rectangle" ) # super() is the class we inherite from
self.width = width
self.length = length
def surface(self):
return self.width * self.length
r = Rectangle(3,2)
print(r.name, r.position, r.width, r.length)
Rectangle [0, 0] 3 2
A child class inherits all attributes from its parent. However a parent object has to be created to store these attributes :
- a Shape object has an attribute
dimension
only if the Shape__init__
method is called - any class which inherit from Rectangle inherits its method
surface()
however it might be useful to redefine it locally (this is named specialization).
Inheritance is transitive, you get properties from you parent, grand-parents etc (Python allows to break transitivity but it is very uncommon). Here Square
inherits from Rectangle
which inherits from Shape
:
class Square(Rectangle):
def __init__(self, width):
super().__init__(width, width)
self.name = "Square" # since the line above made it "Rectangle"
q = Square(5)
print(q.name, q.position, q.width, q.length) # we still have q.length because of the inheritance
print(q.surface())
Square [0, 0] 5 5 25
Multiple inheritance¶
Let's see how we inherit from more than one class and what are the issues.
class A():
def myclass(self):
print("defined in A, I am a", self.__class__)
class B():
def myclass(self):
print("defined in B, I am a", self.__class__)
class C1(A,B): # double inheritance
pass # which method myclass will it use?
class C2(B,A):
pass
c = C1()
c.myclass()
c = C2()
c.myclass()
defined in A, I am a <class '__main__.C1'> defined in B, I am a <class '__main__.C2'>
On peut connaitre l'héritage d'une classe avec la méthode mro()
pour Method Resolution Order :
print(C1.mro()) # the mother of all classes is object, even if it is not shown in the declaration
[<class '__main__.C1'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
# The diamond problem
#
# Which method of A, B and C will D use if not redefined?
#
# A
# / \
# B C
# \/
# D
class A(object):
def f(self):
print("f is defined in A, my class is", self.__class__)
class B(A):
pass
class C(A):
def f(self): # we redefine method inheritates from A
print("f is defined in C, my class is", self.__class__)
class D(B,C): # double inheritance with B first
pass
d = D()
d.f()
D.mro()
f is defined in C, my class is <class '__main__.D'>
[__main__.D, __main__.B, __main__.C, __main__.A, object]
To choose which methods the last class shoud use, Python uses two constraints:
- children precede their parents
- children are kept in the order specified in the tuple of base classes
Hence D has 2 parents but only C defines myclass method, therefore it uses C methods even if B has also this method inheritated from A because it is defined in A but the rule says children precede their parents.
If C did not inherit from A, then B and C whould have been compared without looking at parents and it would be B methods which would be used:
class C():
def f(self):
print("f is defined in C, my class is", self.__class__)
class D(B,C):
pass
d = D()
d.f()
D.mro()
f is defined in A, my class is <class '__main__.D'>
[__main__.D, __main__.B, __main__.A, __main__.C, object]
Appel direct d'une méthode d'une classe parente¶
On aussi avoir l'ordre initial D(B,C)
et appeler la fonction f
de A à la main avec A.f(self)
:
class C(A):
def f(self):
print("f is defined in C, my class is", self.__class__)
class D(B,C):
def f(self):
A.f(self) # if we don't do that, C.f(self) will be called
d = D()
d.f()
D.mro()
f is defined in A, my class is <class '__main__.D'>
[__main__.D, __main__.B, __main__.C, __main__.A, object]
C.f(d) # direct call to f of class C for object d
f is defined in C, my class is <class '__main__.D'>
{{ PreviousNext("01 Classes and objects.ipynb", "03 Scope.ipynb")}}