---
# System prepended metadata

title: 'Polymorphisme: synthèse'

---

# 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. 


|        |       |
| ----   |  ---- |
| ![](https://i.imgur.com/hEyXgIF.png) | ![](https://i.imgur.com/YKWZTBf.png) |

### 4. NB: Ecrasement vs Redéfinition ;-)

* Override => Redéfinition
    * On tire profit de l’héritage, on étend la méthode de la classe-mère (appel à `super()`) __ET__ on garde la même signature
* Overwrite => Ecrasement
    *  On ne tire pas profit de l’hé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 

