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 :
dimension
only if the Shape __init__
method is calledsurface()
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
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:
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]
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")}}