# Polymorphisme: synthèse
## Polymorphisme
* Le mot « polymorphisme » vient du grec et signifie « qui peut prendre plusieurs formes »
* En POO, signifie que le même nom d'opérateur, de fonction ou de méthode peut être utilisé pour différents types d'arguments
* En Python, on distingue le polymorphisme ad hoc, et le polymorphisme par héritage.
---
### 1. Le polymorphisme ad hoc :
* Des objets de classes différentes et indépendantes, mais qui implémentent la même interface publique (même méthodes et attributs publics) sont interchangeables
#### 1.1 Polymorphisme ad hoc d'opérateurs = *operator overloading*
- Overloading = Surcharge
- En Python, seulement grâce aux méthodes magiques
```python
class MyTime:
# Previously defined methods here...
def __add__(self, other):
secs = self.to_seconds() + other.to_seconds()
return MyTime(0, 0, secs)
def __eq__(self, other):
if type(other) is MyTime:
return self.to_seconds() == other.to_seconds()
else:
return False
t1 = MyTime(1, 15, 42)
t2 = MyTime(3, 50, 30)
t3 = t1 + t2
print(t3) # Affiche 05:06:12
print(t1 == t2) # Affiche False
````
#### 1.2 Polymorphisme ad hoc de fonctions
C'est le cas de nombreuses fonctions "built-in" telles que `len` ou `print`.
```python
print(len("Programiz"))
print(len(["Python", "Java", "C"]))
print(len({"Name": "John", "Address": "Nepal"}))
```
#### 1.3 Polymorphisme ad hoc de méthodes
- Grace au __typage dynamique__ de Python ou __typage canard__ ("Duck Typing")
- La sémantique d’un objet est déterminée par l'ensemble de ses méthodes et ses attributs, et non par un type défini et nommé explicitement par le programmeur
- *“If it walks like a duck and it quacks like a duck, then it must be a duck”*
- C'est une spécificité de Python que d'autres languages, tels que Java, ne possèdent pas, i.e. on parle de *typage statique*.
```python
class Duck :
def __init__(self,nom):
self.nom = nom
def talk(self):
return "Quack, quack!"
def fly(self):
return "Flap, flap!"
def __str__(self):
return self.talk() + " " + self.fly()
# ------------
class Person :
def __init__(self,nom) :
self.nom = nom
def talk(self) :
return "I won the elections."
def fly(self) :
return "I'm flying."
def __str__(self):
return "I'm " + self.nom + ". " + self.talk() + " " + self.fly()
# ------------
class Interview :
def __init__(self,thing):
self.t = thing
def __str__(self):
return self.t.nom + ': "' + str(self.t) + '"'
```
```python
donald = Duck("Donald Duck")
interview = Interview(donald)
print(interview)
# Donald Duck: "Quack, quack! Flap flap!
donald = Person("Donald Trump")
interview = Interview(donald)
print(interview)
# Donald Trump: "I'm Donald Trump. I won the elections. I'm flying."
```
:::info
__NB__: *Method overloading*, a way to create multiple methods in the same class with the same name but different arguments, is not possible in Python.
```python=
class A:
def stackoverflow(self, i='some_default_value'):
print 'only method'
ob=A()
ob.stackoverflow(2)
ob.stackoverflow()
```
You can't have two methods with the same name in Python -- and you don't need to.
:::
### 2. Le polymorphisme par héritage => *overriding*
* Overriding = Redéfinition
* Grâce à l'overriding, il est possible de redéfinir, dans une classe-fille, les méthodes héritées d'une classe-mère.
* A chaque endroit où un objet de la classe mère est attendu, on peut utiliser un objet d’une de ces classes filles (interchangeabilité).
```python
class Parent(object):
def __init__(self):
self.value = 5
def get_value(self):
return self.value
class Child(Parent):
# get_value is overriden
def get_value(self):
return self.value + 1
```
### 3. La liaison dynamique
- Liaision dynamique = "délégation"
- C'est le fait qu'un nom de fonction membre d'une classe-mère peut être associé à une fonction membre d'une classe-fille.
- Mécanisme distinct (bien qu'étroitement lié) de l'héritage.
- C'est une forme de polymorphisme : grâce à la liaison dynamique, un même nom de fonction pourra correspondre à des réalisations différentes suivant les classes dérivées.
| | |
| ---- | ---- |
|  |  |
### 4. NB: Ecrasement vs Redéfinition ;-)
* Override => Redéfinition
* On tire profit de l’héritage, on étend la méthode de la classe-mère (appel à `super()`) __ET__ on garde la même signature
* Overwrite => Ecrasement
* On ne tire pas profit de l’héritage ou;
* On n'a pas exactement la même signature (mêmes paramètres).
* La redéfinition réutilisera l'ancienne méthode, tandis que l'écrasement la remplace par une implémentation complètement nouvelle.
### Sources
* https://www.programiz.com/python-programming/polymorphism