Python-POO Partie II
Master SDGLR 2023 [Link] Brahim
Programmation Orientée Objet
Dictionnaire et OO
Descriptors
Protection des attributes
Méthodes POO
Méthode d’instance
Méthodes de classe :Classmethod
Méthodes statiques : Staticmethod
Concepts POO
Héritage
Composition
Polymorphisme
Iterator & Generator
2
Dictionnaire OO
Tout objet a un attribut dictionnaire (__dict__) qui
comporte les attributs propres de l'objet (pas ceux
dont il hérite, mais en incluant les objets redéfinis)
sous la forma clef/valeur.
La recherche d'un attribut se fait dans le ou les
dictionnaires relatifs à l'objet qui utilise cet attribut.
Pour savoir si un attribut appartient à un objet ou ses
ascendants, utiliser la fonction prédéfinie hasattr().
Une fonction globale qui affiche le dictionnaire d'un
objet. On notera que items() retourne un
énumérable (une vue).
3
Dictionnaire OO
Exemple :
4
Dictionnaire OO
Exemple :
Attributs de l'instance : {'nom': 'Alice', 'age': 25}
Attributs de la classe : {'__module__': '__main__', 'attribut_de_classe': 'Partagé par
toutes les instances', '__init__': <function Personne.__init__ at
0x000002869EA68D30>, '__dict__': <attribute '__dict__' of 'Personne' objects>,
'__weakref__': <attribute '__weakref__' of 'Personne' objects>, '__doc__': None}
5
Dictionnaire OO
Exemple :
Attributs de la classe : ['__class__',
Attributs de l'objet : ['__class__', '__delattr__',
'__delattr__', '__dict__', '__dir__', '__doc__',
'__dict__', '__dir__', '__doc__', '__eq__',
'__eq__', '__format__', '__ge__',
'__format__', '__ge__', '__getattribute__',
'__getattribute__', '__gt__', '__hash__',
'__gt__', '__hash__', '__init__',
'__init__', '__init_subclass__', '__le__', '__lt__',
'__init_subclass__', '__le__', '__lt__',
'__module__', '__ne__', '__new__',
'__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__',
'__reduce__', '__reduce_ex__', '__repr__',
'__setattr__', '__sizeof__', '__str__',
'__setattr__', '__sizeof__', '__str__',
'__subclasshook__', '__weakref__',
'__subclasshook__', '__weakref__',
'attribut_de_classe']
'attribut_de_classe', 'nom']
6
Dictionnaire OO
Exemple : >>>foo.__dict__
class MyClass(object): >>>MyClass.__dict__
class_var = 1 >>>foo.__dict__['i_var']
def __init__(self, i_var):
>>>foo.__dict__['i_var']=10
self.i_var = i_var
>>>MyClass.__dict__['class_var']
>>>setattr(foo, 'class_var', 10)
>>>setattr(MyClass, 'class_var', 20)
>>>getattr(MyClass, 'class_var')
>>>getattr(foo, 'i_var')
>>>delattr(MyClass,'class_var')
7
Dictionnaire OO
8
Dictionnaire OO : Cherche un attribut
Il y a plusieurs façons de rechercher un attribut :
Si l'attribut n'existe pas, ces méthodes retourneront
respectivement False, KeyError, default_value ou
AttributeError.
Il est donc important de gérer les exceptions
9
Dictionnaire OO
Exemple : >>> foo=MyClass(2)
class MyClass(object): >>>foo.__dict__
class_var = 1 >>>instlookup(foo,'i_var')
def __init__(self, i_var): >>>2
self.i_var = i_var >>>instlookup(foo,'class_var')
>>>1
def instlookup(inst, name):
if name in inst.__dict__.keys():
return inst.__dict__[name]
else:
return inst.__class__.__dict__[name]
10
Descriptors: Encapsulation
Les modificateurs d'accès limitent l'accès aux variables et aux méthodes
d'une classe.
Python fournit trois types de modificateurs d'accès privé, public et
protégé.
Membre public : Accessible n'importe où depuis l'extérieur de la
classe.
Membre privé : accessible au sein de la classe
Membre protégé : accessible au sein de la classe et de ses sous-classes
11
Descriptors: Définitions
Un attribut qui possède au moins une des trois méthodes
__get__, __set__ ou __del__ est appelée un descripteur.
Un descripteur qui possède la méthode __get__ mais pas la
méthode __set__ est un descripteur sans données (Non-
Data).
L'exemple type de descripteur sans données est la
fonction ; c'est le fait d'être un tel descripteur par défaut
qui permet aux méthodes d'être appelées à la fois depuis
une instance et la classe.
Un descripteur qui possède la méthode __set__ est appelé
un descripteur de données (Data).
Un attribut descripteur est nécessairement un attribut de
classe, jamais un attribut d'instance.
12
Descriptors: Définitions
Il existe de nombreuses raisons pour lesquelles on aimerait
contrôler ce qui se passe lorsqu'un attribut est accédé.
Data validation. ex. verifying value is in some numeric range.
Immutable attribute: attribute setter will raise an exception.
One time set attribute : setter will mark an internal flag that
says that this attribute can not be changed after its first
assignment.
Mutate another dependent attributes. ex. change circle area
attribute when setting the circle radius value.
Lazy evaluation: Calculating attribute values only when
accessed. ex. Reading image from disk only when [Link] is called.
Memoize attribute value that requires heavy value calculation
/ query. ex. Reading a file content again only if file was changed.
Logging access events for debugging.
13
Descriptors: Accès à un attribut
Accéder à un attribut, c'est retrouver son dictionnaire
d'implémentation, où sa clef apparaît comme une entrée.
On peut alors accéder à sa valeur, la modifier ou
supprimer l'attribut.
L'ensemble des attributs disponibles pour un objet (utiliser
la fonction prédéfinie dir() pour obtenir la liste des chaînes
de recherches valides) comporte deux familles :
Les attributs disponibles dans le dictionnaire de l'objet en
tant qu'instance ;
Les attributs disponibles dans le dictionnaire de la classe
de l'objet et dans les dictionnaires de toutes les classes
de l'ascendance.
14
Descriptors: Accès à un attribut
La recherche d'un attribut se fait dans l'ordre suivants :
Le dictionnaire de l'objet en tant qu'instance. On
rappelle qu'en Python, tout objet est une instance !
Le dictionnaire de la classe de l'objet ;
Le dictionnaire de la classe située
hiérarchiquement avant le classe de l'objet et ainsi de
suite jusque la rencontre avec le dictionnaire de object (
l'héritage multiple).
La recherche s'arrête sur la première
occurrence trouvée, occultant les occurrences
éventuelles qui son hiérarchiquement antérieures.
15
Descriptors: Récupération
La recherche en vue de récupérer la valeur d'un
attribut est une méthode __getattribute__() qui, par
défaut, retourne la valeur de l'attribut !
Si l'attribut possède une méthode __get__(), alors la
méthode __getattribute__() retourne l'appel de cette
méthode sur l'attribut reconnu.
La méthode __get__() peut manipuler à loisir la
valeur retournée lorsque l'attribut est évoqué en vue
de récupérer sa valeur .
Les fonctions (et méthodes) ont par défaut cette
méthode __get__().
16
Descriptors: Récupération
La recherche en vue de récupérer la valeur d'un
attribut est une méthode __getattribute__() qui,
par défaut, retourne la valeur de l'attribut !
def __getattribute__(self, key):
"Emulate type_getattro() inObjects/typeobject.c"
v = object.__getattribute__(self, key)
if hasattr(v, '__get__'):
return v.__get__(None, self)
return v
17
Descriptors: Mécanisme
En résumé, il y a trois cas possibles :
L'attribut x ne possède pas l'attribut __get__: alors la
valeur retournée est la valeur brute à laquelle __getattr__ a
accédé dans le dictionnaire ad-hoc par le procédé usuel
d'identification.
L'attribut x possède l'attribut __get__ et objapp a été
identifié comme une classe : alors la valeur retournée par
__getattr__ est celle que retournera la méthode __get__
dans l'exécution de objapp.x.__get__ (arguments pour la
classe app).
L'attribut x possède l'attribut __get__ et objapp a été
identifié comme une instance : alors la valeur retournée
par __getattr__ est celle que retournera la méthode __get__
dans l'exécution de objapp.x.__get__ (arguments pour
l'instance app).
18
Descriptors: Mécanisme
En résumé, il y a trois cas
possibles :
L'attribut x ne
possède pas l'attribut
__get__
L'attribut x possède
l'attribut __get__ et
objapp a été identifié
comme une classe
L'attribut x possède
l'attribut __get__ et
objapp a été identifié
comme une instance
19
Descriptors: Attribute Get in Action
20
Descriptors: getattribute vs getattr
getattribute: est utilisé pour récupérer un attribut d'une instance. Il capture
chaque tentative d'accès à un attribut d'instance en utilisant la notation par
points ou la fonction intégrée getattr().
getattr: est exécuté en tant que dernière ressource lorsque l'attribut n'est
pas trouvé dans un objet. Ce qui donne le choix de retourner une valeur par
défaut ou de lever AttributeError.
21
Descriptors: getattribute vs getattr
Si la classe contient getattr et getattribute méthodes
magiques, getattribute est appelé en premier.
Mais si __getattribute__ déclenche l'exception AttributeError , l'exception
sera ignorée et la méthode __getattr__ sera appelée.
22
Descriptors: getattribute vs getattr
La fonction __getattribute__ si l'implémentation par défaut n'a
pas été remplacée ; les vérifications suivantes sont effectuées
lors de l'exécution de la méthode :
Vérifier s'il existe un descripteur avec le même nom (nom
d'attribut) défini dans n'importe quelle classe de la chaîne MRO
(méthode de résolution d'objet)
Ensuite regarde dans l'espace de noms de l'instance
Ensuite regarde dans l'espace de noms de classe
Puis dans l'espace de noms de chaque base et ainsi de suite.
Enfin, si elle n'est pas trouvée, l'implémentation par défaut
appelle la méthode de secours getattr() de l'instance et
déclenche une exception AttributeError comme
implémentation par défaut.
23
Descriptors: getattribute vs getattr
Une récursion infinie: la ligne return self.__dict__[attr].
La ligne f.a tente d'accéder à l' attribut a de f.
Cela appelle f.__getattribute__('a').
__getattribute__essaie ensuite de charger self.__dict__. __dict__est un attribut
des self == f appels python et donc f.__getattribute__('__dict__')qui essaie à
nouveau d'accéder à l'attribut '__dict__'. C'est une récursion infinie.
Si __getattr__avait été utilisé à la place, Il n'aurait jamais fonctionné car il f possède
un a attribut.
24
Descriptors: Affectation
Le schéma grammatical de l'instruction d'affectation d'un attribut est :
objapp.x = valeur
objapp est l'objet d'appel, x est l'attribut, l'expression valeur est
supposée évaluée ;
C'est cette expression qui est traitée lors de l'interprétation. Elle
comporte deux étapes :
Accéder à l'attribut ;
Changer, dans le dictionnaire ad-hoc la valeur associée à l'attribut en
fonction de la valeur calculée dans l'évaluation de l'expression valeur.
Le traitement de l'expression relève de la méthode __setattribute__
qui est parallèle à __getattribute__.
La valeur associée à l'identificateur x doit changer.
__setattribute__ a donc deux arguments, la clef et la valeur.
25
Descriptors: Mécanisme set
En résumé:
[Link] = valeur : la méthode __setattribute__ recherche
l'attribut selon la méthode vue avec __getattribute__ mais accorde
une précédence à l'attribut doté de la méthode __set__.
Si l'attribut est trouvé avec la méthode __set__ cette méthode
est appelée et fait le travail défini dans la méthode.
Si l'attribut n'est pas trouvé disposant d'une méthode
__set__, alors soit il existe dans une_instance, et on a alors
l'affectation tout à fait classique, soit il n'existe pas dans
une_instance, auquel cas il est créé comme attribut de
une_instance.
une_classe.un_attribut = valeur: l'affectation est directe, sans passer
par la méthode __set__.Dans ce cas, la valeur initiale de l'attribut est
changée pour la nouvelle valeur, sans passer par __set__.
26
Descriptors: Set / Delete in Action
27
Descriptor Example
28
Slots -1-
__slots__ par défaut, chaque instance d’une classe possède
son propre dictionnaire pour y stocker ses attributs.
Lorsque le nombre d’instances est important, cette
particularité est très consommatrice en mémoire vive.
Ce comportement des classes Python peut être modifié par
la déclaration de __slots__.
Dans ce cas, lorsqu’une séquence d’instances de la classe est
créée, seul l’espace mémoire nécessaire aux attributs
déclarés est utilisé, et le dictionnaire n’est pas créé.
__slots__ est une variable de classe à laquelle est affectée une
chaîne, un itérable ou une séquence de chaînes, contenant les
noms des attributs déclarés.
29
Slots -1-
L'attribut spécial __slots__ permet d'indiquer
explicitement les attributs d'instance d'objet, avec les
résultats attendus :
Accès plus rapide aux attributs.
Gain d'espace en mémoire.
Le gain d'espace est de:
Stockage des références de valeur dans des
emplacements au lieu de __dict__.
Pas de création de __dict__ et __weakref__ si les
classes parentes les refusent et alors que __slots__
est déclaré.
30
Slots -2-
Exemple:
31
Slots -3-
Exemple:
Article_slots_dict.__class__.printer= "Alice"
32
Slots -4- : Héritage
Si l’héritage est faite d'une classe qui inclut __slots__, c’est inutile de
répéter ces attributs dans la sous-classe. Sinon, la sous-classe prendra plus
de place.
De plus, les attributs répétés seront inaccessibles dans la classe parent.
33
Slots -4- : Héritage
C’est possible également ajouter l'attribut __dict__ dans la sous-classe. Ou,
on ne met pas __slots__ dans la sous-classe, elle aura par
défaut __dict__.
34
Slots -5-
Il faut pas hériter d'un type intégré tel que int, bytes, tuple avec
des __slots__ non vides.
De plus, il faut pas attribuer de valeur par défaut aux attributs
dans __slots__. En effet, ces attributs sont censés être des descripteurs. Au
lieu de cela, c’est possible attribuer la valeur par défaut dans __init__().
35
Weakref en python
Le garbage collector en Python utilise une technique de
comptage des références pour déterminer si un objet est
toujours accessible.
Si le compteur de références d'un objet tombe à zéro,
cela signifie qu'aucun autre objet dans le programme ne fait
référence à cet objet, et il peut être considéré comme éligible à
la collecte par le garbage collector.
L'utilisation de références faibles (weakref) est une manière de
créer des liens vers des objets sans augmenter leur compteur de
références.
Les références faibles permettent au garbage collector de
collecter les objets même s'ils sont présents dans une structure
de données, évitant ainsi les références circulaires et les
fuites de mémoire.
36
Weakref en python
weakref est un outil puissant pour gérer la durée de vie des objets de manière flexible et
efficace, en particulier dans des scénarios où la collecte automatique des objets non utilisés
par le garbage collector est importante pour éviter des fuites de mémoire:
1. Éviter les Références Circulaires : Les références circulaires se produisent lorsque des objets
se référencent mutuellement, formant une boucle. Cela peut empêcher le garbage collector de
libérer la mémoire, car les objets, bien que non référencés de manière externe, continuent de se
référencer mutuellement.
2. Collecte des Objets Temporaires : Parfois, on crée des objets temporaires ou des caches qui
ne doivent pas prolonger la durée de vie des objets qu'ils référencent. En utilisant des références
faibles, on permet au garbage collector de collecter ces objets temporaires dès qu'ils ne sont plus
référencés de manière forte.
3. Gestion de Caches et de Pools d'Objets : Lors de la mise en œuvre de caches ou de pools
d'objets, on peut utiliser des références faibles pour permettre au garbage collector de libérer
automatiquement les objets qui ne sont plus nécessaires.
4. Gestion d'Objets dans des Structures de Données : Lorsqu'on stocke des objets dans des
structures de données, comme des dictionnaires ou des ensembles, on peut utiliser des références
faibles pour éviter de prolonger la durée de vie de ces objets, permettant ainsi au garbage collector
de les collecter dès qu'ils ne sont plus nécessaires.
5. Éviter les Problèmes de Références Fortes : Les références fortes augmentent le compteur
de références d'un objet, ce qui prolonge sa durée de vie. Dans certaines situations, cela peut causer
des problèmes de gestion de la mémoire. Les références faibles permettent d'éviter ces problèmes
en évitant d'ajouter des références fortes aux objets.
37
Weakref en python
weakref est un module intégré de Python qui permet de créer des références
faibles à des objets.
Les références faibles sont similaires aux références normales, mais elles ne gardent
pas l'objet en question en vie. Cela signifie que si l'objet n'a pas d'autres références
valides, il sera nettoyé par le ramasse-miettes (garbage collector) de Python.
Les références faibles sont utiles dans les cas où vous avez un cycle de références
qui empêche l'objet de se faire nettoyer par le ramasse-miettes.
38
Weakref en python
Il est important de noter que les références faibles ne garantissent pas que
l'objet sera immédiatement nettoyé par le ramasse-miettes lorsque
toutes les références valides ont été supprimées.
La suppression de l'objet peut être retardée jusqu'à ce que le ramasse-
miettes effectue son prochain cycle.
les objets immuables ne nécessitent pas l'utilisation de références faibles car
ils ne causent pas de problèmes de mémoire, tandis que les objets mutables
peuvent causer des problèmes de mémoire.
En raison des cycles de références qu'ils peuvent créer, les références faibles
peuvent être utilisées pour rompre ces cycles de références et permettre au
ramasse-miettes de nettoyer les objets qui ne sont plus utilisés.
Il existe d'autres classes dans le module weakref qui offrent des fonctionnalités
plus avancées, comme WeakKeyDictionary, WeakValueDictionary, et
WeakSet, qui permettent de travailler avec des collections de références
faibles. Ces classes sont utiles pour éviter les références circulaires dans les
structures de données complexes.
39
Weakref en python
Resume
• Each time you create a reference to an object, it is
increased by one, and whenever you delete a
reference, it is decreased by one.
• Weak references allow you to create references to an
object that will not increase the reference count.
• The reference count is used by python's Garbage
Collector when it runs: any object whose reference
count is 0 will be garbage collected.
• You would use weak references for expensive
objects, or to avoid circle references (although the
garbage collector usually does it on its own).
40
Méthodes POO
Définitions:
Une méthode définie dans une classe peut être
de l'un des trois genres suivants, qui, chacun ont
un mode d'appel particulier :
Méthode d'instance (self,……)
Méthode statique (*arg,**kargs)
Méthode de classe (cls,……)
41
Méthodes POO
42
Méthodes POO
Exemple:
43
Méthodes POO : Méthode d'instance
Le mode d'appel d’une méthode d'instance est le mode
par défaut ;
elle est toujours déclarée avec au moins un paramètre usuellement
appelée self.
Avec les modes d'appel, soit à partir d'une instance, soit à partir de la
classe.
Exemple:
class MyClass(object):
class_var = 1 >>>foo=MyClass(100)
def __init__(self, i_var):
>>>[Link]()
self.i_var = i_var
>>>foo.__class__.display(foo)
def display(self):
print(self.i_var)
44
Méthodes POO : Méthode d'instance
Types of Constructors
Default Constructor
Non-parametrized constructor
Parameterized constructor
45
Méthodes POO : Méthode d'instance
Types of Constructors
46
Méthodes POO: Méthode statique
Une méthode est statique : une telle méthode est
appelée depuis une instance ou la classe comme si
c'était une fonction «ordinaire» : les paramètres
formels et les paramètres réels se correspondent
exactement.
47
Méthodes POO: Méthode statique
Exemple:
import datetime
class Date:
def __init__(self, year, month, day):
[Link] = year
>>>[Link](10)
[Link] = month
>>>Working day
[Link] = day
@staticmethod
def holiday(day):
if (day==1):
print("Holiday day“)
else:
print("Working day")
48
Méthodes POO: Méthode de la classe
Un classmethod est similaire à une méthode régulière, sauf que passe la
classe (et non l'instance) comme premier argument de la fonction.
Les arguments restants sont transmis comme d'habitude. Il peut également être
appelé directement sur la classe et il se comporte de la même manière.
Le premier argument est nommé cls au lieu du Self traditionnel pour éviter
toute confusion concernant ce à quoi il se réfère.
49
Méthodes POO: Méthode de la classe
Exemple:
import time
class Date: # Primary constructor
def __init__(self, year, month, day):
[Link] = year
[Link] = month a = Date(2016, 12, 21) # Primary
[Link] = day b = [Link]() # Alternate
# Alternate constructor
@classmethod
def today(cls):
t = [Link]()
return cls([Link], [Link], tday)
50
Héritage
Définition d’héritage:
L’héritage est le mécanisme qui permet de se servir d’une classe préexistante pour en
créer une nouvelle qui possédera des fonctionnalités différentes ou
supplémentaires.
Types Of Inheritance
En Python, en fonction du nombre de classes enfant et parent impliquées, il existe
cinq types d'héritage. Les types d'héritage sont listés ci-dessous
1) Single inheritance 2
2) Multiple Inheritance 1
3) Multilevel inheritance
4) Hierarchical Inheritance
5) Hybrid Inheritance
5
4 3
51
Héritage
Exemple: Dans l’exemple suivant, la classe Carre hérite de
la classe Rectangle, et la méthode init est
polymorphe :
52
Héritage :Exemple
53
Héritage: Implicit Inheritance
class Parent(object):
def implicit(self):
print ("PARENT implicit()")
class Child(Parent):
pass
dad = Parent()
son = Child()
[Link]()
[Link]()
54
Héritage: Override Explicitly
class Parent(object):
def override(self):
print ("PARENT override()")
class Child(Parent):
def override(self):
print ("CHILD override()")
dad = Parent()
son = Child()
[Link](son)
[Link]()
[Link]()
55
Héritage: Alter Before or After
class Parent(object):
def altered(self):
print ("PARENT altered()“)
class Child(Parent):
def altered(self):
print ("CHILD, BEFORE PARENT altered()“)
super(Child, self).altered()
print ("CHILD, AFTER PARENT) altered()“
dad = Parent()
son = Child()
[Link]()
[Link]()
56
Héritage:super()avec init ()
L'utilisation la plus commune de super() est en fait
dans les fonctions init dans des classes de base.
Cela est généralement le seul endroit où vous avez
besoin de faire des choses chez un Child, puis
terminer l'initialisation dans le parent.
Exemple de réalisation que dans Child:
class Child(Parent):
def init (self, stuff):
[Link] = stuff
super(Child, self). init ()
57
Héritage: super()avec init ()
58
Héritage : MRO
Method Resolution Order (MRO)
L'ordre de résolution de méthode (MRO) est une approche adoptée par
un langage de programmation pour résoudre les variables ou les méthodes
d'une classe.
Python a une classe de base intégrée nommée en tant qu'objet. Ainsi, toute
autre classe intégrée ou définie par l'utilisateur par héritage.
L'ordre de résolution de méthode (MRO) se déroule :
Dans le cas d'utilisation d'héritage multiple, l'attribut est d'abord
recherché dans la classe courante. En cas d'échec, le prochain endroit
à rechercher est dans la classe parente, et ainsi de suite.
S'il existe plusieurs classes parentes, l'ordre de préférence est la
profondeur d'abord suivi d'un chemin gauche-droite, c'est-à-dire
DLR.
MRO garantit qu'une classe précède toujours ses parents et, pour plusieurs
parents, conserve l'ordre comme tuple des classes de base.
59
Héritage : MRO
60
Mécanismes d'introspection
Les mécanismes d'introspection sont des fonctionnalités de Python qui
permettent de découvrir les informations sur les objets, les classes et
les modules à l'exécution. Ils peuvent être utilisés pour explorer les
relations d'héritage entre les classes et les attributs des objets, y compris
ceux hérités des classes parentes.
Il existe plusieurs mécanismes d'introspection qui peuvent être utilisés
pour explorer les relations d'héritage et les attributs des classes :
La fonction issubclass() permet de vérifier si une classe est une sous-classe
d'une autre classe.
La fonction isinstance() permet de vérifier si un objet est une instance d'une
classe ou de l'une de ses sous-classes.
La fonction getmembers() de la bibliothèque inspect permet de lister tous les
membres d'une classe, y compris les méthodes et les propriétés héritées.
La fonction getmro() de la bibliothèque inspect permet de récupérer la
méthode de recherche des classes qui est utilisée pour résoudre les relations
d'héritage.
61
Mécanismes d'introspection
Il est important de noter que les mécanismes
d'introspection ne tiennent pas compte des
slots, dans l’utilisation des slots dans les
classes, il est préférable de ne pas utiliser les
slots pour explorer les relations d'héritage et les
attributs des classes.
62
Composition
L'héritage est utile, mais une autre façon de faire exactement la même
chose est juste d'utiliser d'autres classes et modules, plutôt que de
compter sur l'héritage implicite.
class Other(object):
def override(self):
print ("OTHER override()" )
def implicit(self): son = Child()
print ("OTHER implicit()" ) [Link]()
def altered(self):
print ("OTHER altered() " ) [Link]()
class Child(object): [Link]()
def__init__(self):
[Link] = Other()
def implicit(self):
[Link]()
def override(self):
print ("CHILD override() " )
def altered(self):
print "CHILD, BEFORE OTHER altered() " )
[Link]()
print "CHILD, AFTER OTHER altered() " )
63
Polymorphisme
Définition de polymorphisme
Le polymorphisme est la faculté pour une méthode portant le même nom mais
appartenant à des classes distinctes héritées d’effectuer un travail différent.
Cette propriété est acquise par la technique de la surcharge.
64
Polymorphisme
Duck typing is one of the concepts in polymorphism.
65
Polymorphisme
Compile-time (Statique) Runtime (Dynamique)
Également connu sous le nom de surcharge de • Il se produit au moment de l'exécution.
méthode ou surcharge d'opérateur. • Les objets d'une classe peuvent être traités
Il se produit au moment de la compilation. comme des objets de leur classe de base.
Différentes méthodes ou opérateurs portent le • Il est souvent associé à l'héritage, aux
même nom mais ont des signatures différentes interfaces et aux méthodes virtuelles.
(nombre ou types de paramètres
différents).
class Animal:
class Calculatrice:
def faire_son(self): pass
def addition(self, a, b):
class Chien(Animal):
return a + b
def faire_son(self): return "Woof!"
def addition(self, a, b, c):
class Chat(Animal):
return a + b + c
def faire_son(self): return "Meow!"
calc = Calculatrice()
print([Link](2, 3))
def faire_crier(animal): return animal.faire_son()
# Appelle la première méthode
chien = Chien()
print([Link](2, 3, 4))
chat = Chat()
# Appelle la deuxième méthode
print(faire_crier(chien)) # Appelle faire_son de Chien
print(faire_crier(chat)) # Appelle faire_son de Chat
66
Polymorphisme
Exemple :
67
TP1
Employee
–First_name
–Last_name
–Salary
/email :First_name.Last_name@[Link]
Bounus
Num_of_emp
–Constructeur simple
–Constructeur with other Employee
–Fullname
–Descriptor Salary
–Applay_Bounus
–Set_Bounus: classmethod
–Isworkday(day): staticmethod [Link]()==5 or 6
–__str__
–-__rpr__
68
Iterators : Introduction
Des séquences infinies surviennent fréquemment en
informatique.
Par exemple, la séquence de tous les nombres naturels
est une séquence infinie, parce qu'il n'y a pas de «dernier»
nombre naturel.
Cependant, il est impossible de stocker physiquement une
quantité infinie de données. Comment contourner ce
problème?
les itérateurs, les générateurs chacune de ces
constructions est conçue pour représenter des
séquences infinies dans une quantité infinie de mémoire.
69
Iterators
Un itérateur est un objet qui représente une séquence de valeurs.
Exemple: Une classe qui implémente l'interface iterator de
Python. Cet itérateur calcule tous les nombres naturels un par un,
à partir de zéro:
class Naturals():
def __init__(self):
[Link] = 0
def __next__(self):
result = [Link]
[Link] += 1
return result
def __iter__(self):
return self
Il existe deux composants de l'interface itérateur Python: la
méthode __next__et __iter__.
70
Iterators: __next__ Method
La méthode __next__ fait deux choses:
1. Calcule la valeur suivante
2. Vérifie s'il reste des valeurs à calculer
Pour retourner la valeur suivante dans la séquence,
l'itérateur effectue un calcul défini dans la méthode
next.
Lorsqu'il ne reste plus de valeurs à calculer, la méthode
suivante doit élever un type d'exception appelé
StopIteration. Cela signale la fin de la séquence.
Remarque: la méthode suivante définie ci-dessus ne
soulève aucune exception StopIteration.
71
Iterators: Exemple
Les itérateurs sont des objets qui ont des toujours les méthodes __next__
et __iter__:
class CountDown(object): >>> i = iter(range(5, 0, -
def __init__(self, start): 1))
[Link] = start + >>> next(i)
1 5
def __next__(self): >>> next(i)
[Link] -= 1
4
if [Link] <= 0
raise
>>> next(i)
StopIteration 3
return [Link] >>> next(i)
def __iter__(self): 2
return self >>> next(i)
>>> cd =
1
CountDown(5)
>>> next(i)
>>> for x in cd:
Traceback (most recent call
... print(x)
last):
5 4 3 2 1 0
File "<interactive
72 input>", line 1, in
<module>
Iterators: __iter__ Method
Le but de la méthode __iter__ est de renvoyer un objet
itérateur.
Par définition: un Iterator est un objet qui a implémenté
les deux méthodes __next__ et __iter__.
Cela a une conséquence intéressante. Si une classe
implémente une méthode next et Une méthode iter, sa
méthode iter peut simplement retourner self (comme
dans l'exemple).
Puisque la classe implémente à la fois next et iter, elle
est techniquement un objet iterator, donc sa méthode
iter peut simplement revenir elle-même.
73
Generator Functions
Les générateurs peuvent générer des itérateurs.
Les générateurs constituent une classe spéciale de fonctions qui
simplifient la tache dans l'écriture des itérateurs. Les fonctions
régulières calculent une valeur et la retournent, mais les générateurs
retournent un itérateur qui retourne un flux de valeur.
Une fonction de générateur est une fonction avec le rendement de
mot-clé yield dans son corps.
La déclaration de yield est similaire à une déclaration de return .
Cependant, alors qu'une instruction de return provoque la destruction
de l'environnement actuel après la sortie d'une fonction, une
instruction de yield amène l'environnement à être enregistré jusqu'à la
prochaine fois que __next__ est appelé, ce qui permet au
générateur de suivre automatiquement l'état d'itération.
74
Generator Functions
Une fonction de générateur est une fonction avec le rendement de mot-clé
yield dans son corps:
>>> def count_down(value):
>>> next(c)
... for x in range(value, 0, -1):
3
... yield x
>>> next(c)
...
2
>>> c = count_down(5)
>>> next(c)
>>> next(c)
1
5
>>> next(c)
>>> next(c)
Traceback (most recent call last):
4
File "<interactive input>", line 1,
in <module>
StopIteration
75
TP : iterator
Écrire une class qui nous donnera prochaine puissance de 2
dans chaque itération. L'exposant de puissance commence à
partir du zéro jusqu'à un numéro défini par l'utilisateur.
>>> a = PowTwo(4)
>>> i = iter(a)
>>> next(i) 1
>>> next(i) 2
>>> next(i) 4
>>> next(i) 8
>>> next(i) 16
>>> next(i)
Traceback (most recent call last): ...
StopIteration
Écrire un Générateur qui inverse une chaîne.
76