0% ont trouvé ce document utile (0 vote)
5 vues127 pages

Cours Python3 Mars 2026

Le document présente les notions de base du langage Python, y compris les techniques de production de programmes par compilation et interprétation, ainsi que la construction de programmes selon des méthodologies procédurales et orientées objet. Il aborde également les types de données, les identifiants, les mots réservés, et les opérations sur les types de données tels que les entiers, flottants et complexes. Enfin, il explique la notion de variables et d'affectation en Python.

Transféré par

yannicktad
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
5 vues127 pages

Cours Python3 Mars 2026

Le document présente les notions de base du langage Python, y compris les techniques de production de programmes par compilation et interprétation, ainsi que la construction de programmes selon des méthodologies procédurales et orientées objet. Il aborde également les types de données, les identifiants, les mots réservés, et les opérations sur les types de données tels que les entiers, flottants et complexes. Enfin, il explique la notion de variables et d'affectation en Python.

Transféré par

yannicktad
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

LANGAGE PYTHON

CHAPITRE 1

NOTIONS DE BASE

1. Production des programmes


1.1 Deux techniques de production des programmes
La compilation est la traduction du source en langage objet. Elle comprend au moins quatre
phases (trois phases d’analyse — lexicale, syntaxique et sémantique — et une phase de
production de code objet). Pour générer le langage machine il faut encore une phase particulière
: l’édition de liens. La compilation est contraignante mais offre au final une grande vitesse
d’exécution.

Figure 1.1 – Chaîne de compilation

Dans la technique de l’interprétation chaque ligne du code source analysé est traduite au fur
et à mesure en instructions directement exécutées. Aucun programme objet n’est généré. Cette
technique est très souple mais les codes générés sont peu performants : l’interpréteur doit être
utilisé à chaque nouvelle exécution.. .

Figure 1.2 – Technique de l’interprétation

1.2 Technique de production de Python


• Technique mixte : l’interprétation du bytecode compilé. Bon compromis entre la facilité de
développe ment et la rapidité d’exécution ;
• le bytecode (forme intermédiaire) est portable sur tout ordinateur muni de la machine
virtuelle Python.

Figure 1.3 – Interprétation du bytecode compilé

Pour exécuter un programme, Python charge le fichier source .py (ou .pyw) en mémoire vive,
en fait l’analyse (lexicale, syntaxique et sémantique), produit le bytecode et enfin l’exécute.
Afin de ne pas refaire inutilement toute la phase d’analyse et de production, Python
sauvegarde le bytecode produit (dans un fichier .pyo ou .pyc) et recharge simplement le fichier
bytecode s’il est plus récent que le fichier source dont il est issu.
En pratique, il n’est pas nécessaire de compiler explicitement un module, Python gère ce
mécanisme de façon transparente.
1.3 La construction des programmes
Le génie logiciel étudie les méthodes de construction des programmes. Plusieurs modèles sont
envisageables, entre autres :

1. la méthodologie procédurale. On emploie l’analyse descendante (division des problèmes)


et remontante (réutilisation d’un maximum de sous-algorithmes). On s’efforce ainsi de
décomposer un problème complexe en sous-programmes plus simples. Ce modèle
structure d’abord les actions ;
2. la méthodologie objet. Centrée sur les données, elle est considérée plus stable dans le
temps et meilleure dans sa conception. On conçoit des fabriques (classes) qui servent à
produire des composants (objets) qui contiennent des données (attributs) et des actions
(méthodes). Les classes dérivent (héritage et polymorphisme) de classes de base dans une
construction hiérarchique.
Python offre les deux techniques.

Algorithme et programme
ii. Définitions
Définition
Algorithme : ensemble des étapes permettant d’atteindre un but en répétant un nombre fini
de fois un nombre fini d’instructions.
Un algorithme se termine en un temps fini.
Définition
Programme : un programme est la traduction d’un algorithme en un langage compilable ou
inter- prétable par un ordinateur.
Il est souvent écrit en plusieurs parties dont une qui pilote les autres : le programme principal.

iii. La présentation des programmes


Un programme source est destiné à l’être humain. Pour en faciliter la lecture, il doit être
judicieusement
présenté et commenté.

La signification de parties non triviales (et uniquement celles-ci) doit être expliquée par un
commentaire. En Python, un commentaire commence par le caractère # et s’étend jusqu’à la fin
de la ligne :

#
# Voici un commentaire
#

9 + 2 # En voici un autre

b. Les implémentations de Python


Remarque

✓ Une « implémentation » signifie une « mise en œuvre ».

• CPython : Classic Python, codé en C, implémentation portable sur différents systèmes


• Jython : ciblé pour la JVM (utilise le bytecode de JAVA)
• IronPython : [Link], écrit en C#, utilise le MSIL (MicroSoft Intermediate Language)
• Stackless Python : élimine l’utilisation de la pile du langage C (permet de récurser tant
que l’on veut)
• Pypy : projet de recherche européen d’un interpréteur Python écrit en Python
2. La calculatrice Python

Comme tout langage, Python permet de manipuler des données grâce à un


vocabulaire de mots réservés et grâce à des types de données – approximation des
ensembles de définition utilisés en mathématique.

Cette partie présente les règles de construction des identificateurs, les types
de données simples (les conteneurs seront examinés ultérieurement) ainsi que les
types chaîne de caractères.

Enfin, elle s’étend sur les notions non triviales de variable, de référence d’objet
et d’affectation.

2.1 Les modes d’exécution


2.1.1 Les deux modes d’exécution d’un code Python
• Soit on enregistre un ensemble d’instructions Python dans un fichier grâce à un éditeur (on
parle alors d’un script Python) que l’on exécute par une commande ou par une touche du
menu de l’éditeur ;
• soit on utilise l’interpréteur Python embarqué qui exécute la boucle d’évaluation (☞ Fig. 2.1).

Figure 2.1 – La boucle d’évaluation de l’interpréteur Python

2.2 Identificateurs et mots clés


2.2.1 Identificateurs
Comme tout langage, Python utilise des identificateurs pour nommer ses objets.
Définition
Un identificateur Python est une suite non vide de caractères, de longueur quelconque,
formée d’un caractère de début et de zéro ou plusieurs caractères de continuation.
Sachant que :
• le caractère de début peut être n’importe quelle lettre Unicode (cf. annexe C), y compris
le caractère souligné (_) ;
• un caractère de continuation est un caractère de début ou un chiffre.
Attention
Les identificateurs sont sensibles à la casse et ne doivent pas être un mot réservé de Python
3.

2.2.2 Style de nommage


Il est important d’utiliser une politique cohérente de nommage des identificateurs. Voici le style
utilisé dans ce document 1 :
• UPPERCASEou UPPER_CASE pour les constantes ;
• TitleCase pour les classes ;
• UneExceptionError pour les exceptions ;
• camelCase pour les fonctions et les méthodes ;
• unmodule_m pour les modules ;
• lowercase ou lower_case pour tous les autres identificateurs.
Exemples :
NB_ITEMS = 12 # UPPER_CASE
class MaClasse : pass # TitleCase
def maFonction() : pass # camelCase
mon_id = 5 # lower_case

Pour ne pas prêter à confusion, éviter d’utiliser les caractères l (minuscule), O et I (majuscules)
seuls.
Enfin, on évitera d’utiliser les notations suivantes :
_xxx # usage interne
xxx # attribut de classe
xxx # nom spécial réservé

2.2.3 Les mots réservés de Python 3


La version 3.3.0 de Python compte 33 mots clés :

and del from None True


as elif global nonlocal try
assert else if not while
break except import or with
class False in pass yield
continue finally is raise
def for lambda return

2.3 Notion d’expression


Définition

Une expression est une portion de code que l’interpréteur Python peut évaluer pour
obtenir une valeur.
Les expressions peuvent être simples ou complexes. Elles sont formées d’une combinaison de
littéraux représentant directement des valeurs, d’identificateurs et d’opérateurs.

Exemples de deux expressions simples et d’une expression complexe :

>>> id1 = 15.3

>>>

>>> id2 = maFonction(id1)

>>>

>>> if id2 > 0:

... id3 = [Link](id2)

... else :

1. Voir les détails dans la PEP 8 : « Style Guide for Python », Guido van R. et Barry W.
7

2.4 Les types de données entiers


Python 3 offre deux types entiers standard : int et bool.

2.4.1 Le type int

Le type int n’est limité en taille que par la mémoire de la machine 1.


Les entiers littéraux sont décimaux par défaut, mais on peut aussi utiliser les bases
suivantes (cf. annexe D) :

>>> 2013 # décimal


2013
>>> 0b11111011101 # binaire
2013
>>> 0o3735 # octal
2013
>>> 0x7dd # hexadecimal
2013

Opérations arithmétiques

Les principales opérations :


>>> 20 + 3 # Addition
23
>>> 20 – 3 # Soustraction
17
>>> 20 * 3 # Multiplication
60
>>> 20 ** 3 # Puissance
8000
>>> 20 / 3 # Divsion
6.666666666666667
>>> 20 // 3 # division entière

6
>>> 20 % 3 # modulo

>>> abs(3 - 20) # valeur absolue


17

Bien remarquer le rôle des deux opérateurs de division :


/ : produit une division flottante, même entre deux entiers ;
// : produit une division entière.

Bases usuelles

Un entier écrit en base 10 (par exemple 179) peut se représenter en binaire, octal et hexadécimal
en utilisant les syntaxes suivantes :
>>> 0b10110011 # binaire
179
>>> bin(179)
'0b10110011'
>>> 0o263 # octal
179
>>> oct(179)
'0o263'
>>> 0xB3 # hexadécimal
179
>>> hex(179)
'0xb3'

1. Dans la plupart des autres langages les entiers sont codés sur un nombre fixe de bits et ont un domaine de
définition limité auquel il convient de faire attention.
8

2.4.2 Le type bool

Principales caractéristiques du type bool1 :


• Deux valeurs possibles : False, True.
• Opérateurs de comparaison entre deux valeurs comparables, produisant un résultat de
type bool :
==, !=, >, >=, < et <= :

>>> 2 > 8
False
>>> 2 <= 8 < 15
True

• Opérateurs logiques : not, or et and.


En observant les tables de vérité des opérateurs and et or, on remarque que :
– dès qu’un premier membre a la valeur False, l’expression False and expression2 vaudra False.
On n’a donc pas besoin d’évaluer expression2;
– de même dès qu’un premier membre a la valeur True, l’expression True or expression2 vaudra
True.
Cette optimisation est appelée « principe du shortcut » ou évaluation « au plus court » :
>>> (3 == 3) or (9 > 24)
True
>>> (9 > 24) and (3 == 3)
False

En Python les valeurs des variables booléennes sont notées False et True. Les
opérateurs sont notés respectivement not, and et or.

a not(a)

False True

True False

a b a or b a and b

False False False False

False True True False

True False True False

True True True True

Attention
Pour être sûr d’avoir un résultat booléen avec une expression reposant sur des valeurs
transtypées, appliquez bool() sur l’expression.

2.5 Les types de données flottants


Remarque
✓ La notion mathématique de réel est une notion idéale impossible à mettre en œuvre en informatique.
On utilise une représentation interne permettant de déplacer la virgule grâce à une valeur d’exposant
variable. On nommera ces nombres des flottants.

2.5.1 Le type float

• Un float est noté avec un point décimal (jamais avec une virgule) ou en notation
exponentielle avec un « e» symbolisant le « 10 puissance » suivi des chiffres de
l’exposant. Par exemple :
2.718
.02
-1.6e-19
6.023e23

1. Nommé d’après George B, logicien et mathématicien du 19esiècle.


9

• Les flottants supportent les mêmes opérations que les entiers.


• Ils ont une précision finie limitée.
• L’import du module math autorise toutes les opérations mathématiques usuelles. Par exemple :
>>> import math
>>> print([Link]([Link]/4))
0.7071067811865475
>>>> print([Link]([Link]))
180.0
>>>> print([Link](9))
362880
>>> print([Link](1024, 2))
10.0

2.5.2 Le type complex

• Les complexes sont écrits en notation cartésienne formée de deux flottants.


• La partie imaginaire est suffixée par j :
>>> print(1j)
1j
>>> print((2+3j) + (4-7j))
(6-4j)
>>> print((9+5j).real)
9.0
>>> print((9+5j).imag)
5.0
>>> print((abs(3+4j))) # module
5.0

• Un module mathématique spécifique (cmath) leur est réservé :


>>> import cmath
>>> print([Link](-1 + 0j))
3.141592653589793
>>> print([Link](3 + 4j))
(5.0, 0.9272952180016122)
>>> print([Link](1., [Link]/4))
(0.7071067811865476+0.7071067811865475j)

2.6 Variables et affectation


2.6.1 Les variables
Dès que l’on possède des types de données, on a besoin des variables pour stocker les données.
En réalité, Python n’offre pas directement la notion de variable, mais plutôt celle de référence
d’objet. Tant que l’objet n’est pas modifiable (comme les entiers, les flottants, les chaînes etc.), il
n’y a pas de différence notable entre les deux notions. On verra que la situation change dans le
cas des objets modifiables. . .

Définition
Une variable est un identificateur associé à une valeur. En Python, c’est une référence d’objet.

2.6.2 L’affectation
Définition

On affecte une variable par une valeur en utilisant le signe = (qui n’a rien à voir avec l’égalité
en math !). Dans une affectation, le membre de gauche reçoit le membre de droite ce qui
nécessite d’évaluerla valeur correspondant au membre de droite avant de l’affecter au membre
de gauche.

import math

a = 2
b = 7.2 * [Link](math.e / 45.12) - 2*[Link]
c = b ** a
Remarque
✓ À l’oral, au lieu de dire « a égal 2 », dites « a reçoit 2 ».

La valeur d’une variable, comme son nom l’indique, peut évoluer au cours du temps. La
valeur an térieure est perdue :

>>> a = 3 * 7
>>> a
21
>>> b = 2 * 2
>>> b
4
>>> a = b + 5
>>> a
9

Le membre de droite d’une affectation étant évalué avant de réaliser l’affectation elle-même, la
variable affectée peut se trouver en partie droite et c’est sa valeur avant l’affectation qui est
utilisée dans le calcul :

>>> a = 2
>>> a = a + 1 # incrémentation
>>> a
3
>>> a = a - 1 # décrémentation
>>> a
2

2.6.3 Affecter n’est pas comparer !


L’affectation a un effet (elle modifie l’état interne du programme en cours d’exécution) mais n’a
pas de valeur (on ne peut pas l’utiliser dans une expression) :
>>> c = True
>>> s = (c = True) and True
File
>>> c ="<stdin>",
"a" line 1
>>>ss==(c
(c===True)
"a")and
andTrue
True
>>> s
SyntaxError : invalid syntax
True

La comparaison a une valeur (de type bool) utilisable dans une expression mais n’a pas d’effet :

2.6.4 Les variantes de l’affectation


Outre l’affectation simple, on peut aussi utiliser les formes suivantes :
>>> v = 4 # affectation simple
>>> # affectation augmentée
>>> v += 2 # idem à : v = v + 2 si v est déjà référencé
>>> v
6
>>> c = d = 8 # affectation de droite à gauche : d reçoit 8 puis c reçoit d
>>> c
8
>>> d
8
>>> # affectations parallèles d'une séquence
>>> e, f = 2.7, 5.1 # tuple
>>> (e, f)
(2.7, 5.1)
>>> g, h, i = ['G', 'H', 'I'] # liste
>>> [g, h, i]
['G', 'H', 'I']
>>> x, y = coordonneesSouris() # retour multiple d'une fonction
2.7 Les chaînes de caractères 11

(a) Trois affectations (b) La donnée 'c' est supprimée lorsque z


référence la donnée 'b'

(c) La donnée 'a' est supprimée lorsque x


référence la nouvelle donnée 'd'

Figure 2.2 – L’affectation illustrée.

2.6.5 Représentation graphiques des affectations


Dans les schémas de la figure 2.2, les cercles représentent les identificateurs alors que les
rectangles représentent les données.
Les affectations relient les identificateurs aux données : si une donnée en mémoire n’est
plus reliée, le ramasse-miettes (garbage collector) de Python la supprime automatiquement :

2.7 Les chaînes de caractères


2.7.1 Présentation
Définition
Les chaînes de caractères : le type de données non modifiable str représente une séquence de
caractères Unicode.
Non modifiable signifie qu’une donnée, une fois créée en mémoire, ne pourra plus être changée,
toute transformation résultera en la création d’une nouvelle valeur distincte.

Trois syntaxes de chaîne sont disponibles.


Remarquez que l’on peut aussi utiliser le ' à la place de ”, ce qui permet d’inclure une notation dans l’autre
:

>>> syntaxe1 = "Première forme avec un retour à la ligne \n"


>>> print(syntaxe1)
Première forme avec un retour à la ligne

>>> syntaxe2 = r"Deuxième forme sans retour à la ligne \n"


>>> print(syntaxe2)
Deuxième forme sans retour à la ligne \n
>>> s = """
... Troisième forme multi-lignes
... """
>>>
>>> print(s)

Troisième forme multi-lignes

>>> guillemets = "L'eau vive"


>>> print(guillemets)
L'eau vive
>>> apostrophes = 'Il a dit "gère !"'
>>> print(apostrophes)
Il a dit "gère !"

2.7.2 Opérations
• Longueur :
>>> s = "abcde"
>>> len(s)
5

• Concaténation :
>>> s1 = "abc"
>>> s2 = "defg"
>>> s3 = s1 + s2
>>> s3
'abcdefg'

• Répétition :

>>> s5 = s4 * 3
>>> s5

2.7.3 Fonctions vs méthodes


On peut agir sur une chaîne en utilisant des fonctions (notion procédurale) communes à tous
les types séquences ou conteneurs, ou bien des méthodes (notion objet) spécifiques aux
chaînes.
• Exemple d’utilisation de la fonction len():
>>> lng = len("abc")
>>> lng
3

• Exemple d’utilisation de la méthode upper(). Remarquez la différence de syntaxe : les


méthodes utilisent la notation pointée :
>>> "abracadabra".upper()
"ABRACADABRA"

2.7.4 Méthodes de test de l’état d’une chaîne


Les méthodes suivantes sont à valeur booléenne, c’est-à-dire qu’elles retournent la valeur True ou
False.
Remarque
✓ La notation [xxx] indique un élément optionnel que l’on peut donc omettre lors de l’utilisation
de la méthode.
La chaîne s = ”cHAise basSe” nous servira de test pour toutes les méthodes de cette section.
• isupper() et islower() :
retournent True si la chaîne ne contient respectivement que des
majuscules/minuscules :
>>> [Link]()
False

• istitle() : retourne True si seule la première lettre de chaque mot de la chaîne est en majuscule
>>> [Link]()
False

:
• isalnum(), isalpha(), isdigit() et isspace() :
retournent True si la chaîne ne contient respective ment
que des caractères alphanumériques, alphabétiques, numériques ou des espaces :
>>> [Link]()
True
>>> [Link]()
False
2.7 Les chaînes de caractères 13

• startswith(prefix[, start[, stop]]) et endswith(suffix[, start[, stop]]) : testent si la sous- chaîne définie par
start et stop commence respectivement par prefix ou finit par suffix:
>>> [Link]('cH')
True
>>> [Link]('aSSe')
False

2.7.5 Méthodes retournant une nouvelle chaîne


• lower(), upper(), capitalize() et swapcase():
retournent respectivement une chaîne en minuscule,
en majuscule, en minuscule commençant par une majuscule, ou en casse inversée :
>>> [Link]()
chaise basse
>>> [Link]()
CHAISE BASSE
>>> [Link]()
Chaise basse
>>> [Link]()
ChaISE BASsE

• expandtabs([tabsize]): remplace les tabulations par tabsize espaces (8 par défaut).


• et rjust(width[, fillchar]) : retournent respectivement une chaîne
center(width[, fillchar]), ljust(width[, fillchar])
centrée, justifiée à gauche ou à droite, complétée par le caractère fillchar(ou par l’espace
par défaut) :
>>> [Link](20, '-')
----cHAise basSe----
>>> [Link](20, '@')
@@@@@@@@cHAise basSe

• zfill(width) : complète ch à gauche avec des 0 jusqu’à une longueur maximale de width :

>>> [Link](20)
00000000cHAise basSe

• strip([chars]), lstrip([chars])et rstrip([chars]):


suppriment toutes les combinaisons de chars
(ou l’espace par défaut) respectivement au début et en fin, au début, ou en fin d’une chaîne :
>>> [Link]('ce')
HAise basS

• : renvoie l’index de la chaîne sub dans la sous-chaîne start à stop, sinon


find(sub[, start[, stop]])
renvoie -1. rfind() effectue le même travail en commençant par la fin. index() et rindex() font de
même mais produisent une erreur (exception) si la chaîne n’est pas trouvée :
>>> [Link]('se b')

• replace(old[, new[, count]]) : remplace count instances (toutes par défaut) de old par new:
>>> [Link]('HA', 'ha')
chaise basSe

• split(seps[, maxsplit]) :
découpe la chaîne en maxsplit morceaux (tous par défaut). rsplit() effectue la
même chose en commençant par la fin et striplines()effectue ce travail avec les caractères de
fin de ligne :
>>> [Link]()
['cHAise', 'basSe']

• join(seq) :
concatène les chaînes du conteneur seq en intercalant entre chaque élément la
chaîne sur laquelle la méthode est appliquée :
>>> "**".join(['cHAise', 'basSe'])
cHAise**basSe
2.7.6 Indexation simple

>>> s = "Rayon X" # len(s) ==> 7


>>> s[0]
'R'
>>> s[2]
'y'
>>> s[-1]
'X'
>>> s[-3]
'n'

Pour indexer une chaîne, on utilise l’opérateur [ ] dans lequel l’index, un entier signé qui
commence à 0 indique la position d’un caractère :

Figure 2.3 – L’indexation d’une chaîne.

2.7.7 Extraction de sous-chaînes


Définition
Extraction de sous-chaînes. L’opérateur [ ] avec 2 ou 3 index séparés par le caractère : permet
d’extraire des sous-chaînes (ou tranches) d’une chaîne.

Figure 2.4 – Extraction de sous-chaînes.


Par exemple :
>>> s = "Rayon X" # len(s) ==> 7
>>> s[1:4] # de l'index 1 compris à 4 non compris
'ayo'
>>> s[-2:] # de l'index -2 compris à la fin
' X'
>>> s[:3] # du début à l'index 3 non compris
'Ray'
>>> s[3:] # de l'index 3 compris à la fin
'on X'
>>> s[::2] # du début à la fin, de 2 en 2
'RynX'

2.8 Les données binaires


Les types binaires
Python 3 propose deux types de données binaires : bytes (non modifiable) et bytearray(modifiable).
15

>>> mot = "Animal"


>>> type(mot)
<class 'str'>
>>>
>>> b_mot = b"Animal"
>>> type(b_mot)
<class 'bytes'>
>>>
>>> bmot = bytearray(b_mot)
>>> type(bmot)
<class 'bytearray'>

Une donnée binaire contient une suite de zéro ou plusieurs octets, c’est-à-dire d’entiers non
signés sur 8 bits (compris dans l’intervalle [0…255]). Ces types « à la C » sont bien adaptés pour
stocker de grandes quantités de données. De plus Python fournit des moyens de manipulation
efficaces de ces types.

Les deux types sont assez semblables au type str et possèdent la plupart de ses méthodes.
Le type modifiable bytearray possède des méthodes communes au type list.

2.9 Les entrées-sorties


L’utilisateur a besoin d’interagir avec le programme (☞ Fig. 2.5). En mode « console » (on abordera
les interfaces graphiques ultérieurement), on doit pouvoir saisir ou entrer des informations, ce qui
est généralement fait depuis une lecture au clavier. Inversement, on doit pouvoir afficher ou sortir
des informations, ce qui correspond généralement à une écriture sur l’écran.

(a) Entrée (b) Sortie

Figure 2.5 – Les entrées-sorties.

2.9.1 Les entrées


Il s’agit de réaliser une saisie au clavier : la fonction standard input() interrompt le programme,
affiche une éventuelle invite à l,’écran et attend que l’utilisateur entre une donnée au clavier
(affichée à l’écran) et la valide par sEntrée r
La fonction standard input() effectue toujours une saisie en mode texte (la valeur retournée est
une chaîne) dont on peut ensuite changer le type (on dit aussi transtyper) :
>>> f1 = input("Entrez un flottant : ")
Entrez un flottant : 12.345
>>> type(f1)
<class 'str'>
>>> f2 = float(input("Entrez un autre flottant : "))
Entrez un autre flottant : 12.345
>>> type(f2)
<class 'float'>

2.9.2 Les sorties


En mode « calculatrice », Python lit-évalue-affiche (☞ Fig. 2.1), mais la fonction print() reste indis-
pensable aux affichages dans les scripts. Elle se charge d’afficher la représentation textuelle des
informations qui lui sont données en paramètre, en plaçant un blanc séparateur entre deux
informations, et en faisant un retour à la ligne à la fin de l’affichage (le séparateur et la fin de
ligne peuvent être modifiés) :
16

>>> a, b = 2, 5
>>> print(a, b)
2 5
>>> print("Somme :", a + b)
Somme 7
>>>> print(a - b, "est la différence")
-3 est la différence
>>> print("Le produit de", a, "par", b, "vaut :", a * b)
Le produit de 2 par 5 vaut 10
>>> print()

>>> print("On a <", 2**32, "> cas !", sep="###")


On a <###4294967296###> cas !
>>> # pour afficher autre chose qu'un espace en fin de ligne :

2@5

2.9.3 Les séquences d’échappement


À l’intérieur d’une chaîne, le caractère antislash (\) permet de donner une signification spéciale
à certaines séquences de caractères

Séquence Signification
\saut_ligne saut de ligne ignoré (en fin de ligne)
\\ affiche un antislash
\' apostrophe
\" guillemet
\a sonnerie (bip)
\b retour arrière
\f saut de page
\n saut de ligne
\r retour en début de ligne
\t tabulation horizontale
\v tabulation verticale
\N{nom} caractère sous forme de code Unicode
nommé
\uhhhh caractère sous forme de code Unicode 16
bits
\Uhhhhhhhh caractère sous forme de code Unicode 32
bits
\ooo caractère sous forme de code octal
\xhh caractère sous forme de code hexadécimal

Exemples :

>>> print("\N{pound sign} \u00A3 \U000000A3")


£ £ £
>>> print("d \144 \x64")
d d d
>>> print(r"d \144 \x64")
d \144 \x64

D’autres exemples d’affichage de caractères Unicode sont donnés en annexe C.


CHAPITRE 2

Le contrôle du flux d’instructions

Un script Python est formé d’une suite d’instructions exécutées en séquence de haut en bas a.

Chaque ligne d’instructions est formée d’une ou plusieurs lignes physiques qui
peuvent être continuées par un antislash \ou un caractère ouvrant [({pas encore
fermé.

Cette exécution en séquence peut être modifiée pour choisir ou répéter des
portions de code, ce que l’on appelle des « instructions composées ».
a. On peut mettre plusieurs instructions sur la même ligne en les séparant avec un « ; »
mais, par soucis de lisibilité, c’est déconseillé.

3.1 Les instructions composées


Pour identifier les instructions composées, Python utilise la notion d’indentation significative,
c’est-à-dire visuelle. Cette syntaxe légère met en lumière un bloc d’instructions et permet
d’améliorer grandement la présentation des programmes sources.
Syntaxe
Une instruction composée se compose :
• d’une ligne d’en-tête terminée par deux-points ;
• d’un bloc d’instructions indenté par rapport à la ligne d’en-tête.

Attention
Toutes les instructions au même niveau d’indentation appartiennent au même bloc (☞ Fig. 3.1).

Exemple :
>>> # ...
...
>>> n = -3
>>> if n < 0:
... print("Votre nombre est négatif.")
...
Votre nombre est négatif.
>>> # ... suite du programme

On a souvent besoin d’imbriquer les instructions composées :


>>> # ...
...
>>> n = 3
18 Le contrôle du flux d’instructions

>>> if n <= 0:
... print("Votre nombre est négatif ou nul.")
... else :
... print("Votre nombre est positif.")
... if n > 2:
... print("Il est supérieur à 2.")
...
Votre nombre est positif.
Il est supérieur à 2.
>>> # ... suite du programme

(a) simple (b) imbriquée

Figure 3.1 – Instruction composée.

3.2 Choisir
3.2.1 Choisir : if - [elif] - [else]
Contrôler une alternative :

>>> x = 5
>>> if x < 0:
... print("x est négatif. Pour sa valeur absolue, on prend l'opposé")
... elif x % 2:
... print("x est positif et impair")
... else :
... print("x n'est pas négatif et est pair")
...
x est positif et impair

Test d’une valeur booléenne :

>>> if estPair : # mieux que if estPair == True :


... print("La condition est vraie")

3.2.2 Syntaxe compacte d’une alternative


Pour trouver, par exemple, le minimum de deux nombres, on peut utiliser l’opérateur ternaire :

>>> x, y = 4, 3
>>> if x < y : # écriture classique
... plus_petit = x
... else :
... plus_petit = y
...
>>> print("Plus petit : ", plus_petit)
Plus petit 3
>>>
>>> plus_petit = x if x < y else y # utilisation de l'opérateur ternaire
19

>>> print("Plus petit : ", plus_petit)


Plus petit 3

Remarque
✓ L’opérateur ternaire est une expression qui fournit une valeur que l’on peut utiliser dans une
affectation ou un calcul.

3.3 Boucles

Notions de conteneur et d’itérable


De façon générale, nous parlerons de conteneur pour désigner un
type de données permettant de stocker un ensemble d’autres
données, en ayant ou non, suivant les types, une notion d’ordre
entre ces données.
Nous parlerons aussi d’itérable pour désigner un conteneur que
l’on peut parcourir élément par élément.
Pour parcourir ces conteneurs, nous nous servirons parfois de
l’instruction range() qui fournit un moyen commode pour générer une
liste de valeurs.
Par exemple :
>>> uneListe = list(range(6))
>>> uneListe
[0, 1, 2, 3, 4, 5]

Ces notions seront étudiées plus en détail ultérieurement.

Python propose deux sortes de boucles.

3.3.1 Répéter : while

Répéter une portion de code tant qu’une expression booléenne est vraie :

>>> x, cpt = 257, 0


>>> sauve = x
>>> while x > 1:
... x = x//2 # division avec troncature
... cpt = cpt + 1 # incrémentation
...
>>> print("Approximation de log2 de", sauve, " :", cpt)
Approximation de log2 de 257 8

Utilisation classique : la saisie filtrée d’une valeur numérique (on doit préciser le type car on
se rappelle que input() saisit une chaîne) :

n = int(input('Entrez un entier [1 .. 10] : '))


while not(1 <= n <= 10) :
n = int(input('Entrez un entier [1 .. 10], S.V.P. : '))

3.3.2 Parcourir : for

Parcourir un itérable :

>>> for lettre in "ciao" :


... print(lettre)
...
c
i
a
o
>>> for x in [2, 'a', 3.14]:
... print(x)
...
20 Le contrôle du flux d’instructions

a
3.14
>>> for i in range(5) :
... print(i)
...
0

>>>
>>> nb_voyelles = 0
>>> for lettre in "Python est un langage tres sympa" :
... if lettre in "aeiouy" :
... nb_voyelles = nb_voyelles + 1
...
>>> nb_voyelles
10

3.4 Ruptures de séquences


3.4.1 Interrompre une boucle : break

Sort immédiatement de la boucle for ou while en cours contenant l’instruction :

>>> for x in range(1, 11) :


... if x == 5:
... break
... print(x, end=" ")
...
1 2 3 4
>>> print("Boucle interrompue pour x =", x)
Boucle interrompue pour x = 5

3.4.2 Court-circuiter une boucle : continue

Passe immédiatement à l’itération suivante de la boucle for ou while en cours contenant


l’instruction ; reprend à la ligne de l’en-tête de la boucle :

>>> for x in range(1, 11) :


... if x == 5:
... continue
... print(x, end=" ")
...
1 2 3 4 6 7 8 9 10
>>> # la boucle a sauté la valeur 5

3.4.3 Utilisation avancée des boucles


La syntaxe complète des boucles autorise des utilisations plus rares.

while - else

Les boucles while et for peuvent posséder une clause else qui ne s’exécute que si la boucle se
termine normalement, c’est-à-dire sans interruption :

y = int(input("Entrez un entier positif : "))


while not(y > 0) :
y = int(input('Entrez un entier positif, S.V.P. : '))

x = y // 2
while x > 1:
if y % x == 0:
print(x, "a pour facteur", y)
break # voici l'interruption !
3.4 Ruptures de séquences 21

x -= 1
else :
print(y, "est premier.")

for - else

Un exemple avec le parcours d’une liste :

une_sequence = [2, 5, 9, 7, 11]

cible = int(input("Entrez un entier : "))

for i in une_sequence :
if i == cible :
sauve = i
break # voici l'interruption !
else :
print(cible, "n'est pas dans", une_sequence)
sauve = None

# sauve vaut donc cible ou None :


print("On obtient sauve =", sauve)

3.4.4 Traitement des erreurs — les exceptions


Afin de rendre les applications plus robustes, il est nécessaire de gérer les erreurs d’exécution
des parties sensibles du code.
Le mécanisme des exceptions sépare d’un côté la séquence d’instructions à exécuter
lorsque tout se passe bien et, d’un autre côté, une ou plusieurs séquences d’instructions à
exécuter en cas d’erreur.
Lorsqu’une erreur survient, un objet exception est passé au mécanisme de propagation des
exceptions, et l’exécution est transférée à la séquence de traitement ad hoc.
Le mécanisme s’effectue en deux phases :
• la levée d’exception lors de la détection d’erreur ;
• le traitement approprié.
Syntaxe
La séquence normale d’instructions est placée dans un bloc try.
Si une erreur est détectée (levée d’exception), elle est traitée dans le bloc except
approprié (le gestionnaire d’exception).

from math import sin

for x in range(-4, 5) : # -4, -3, -2, -1, 0, 1, 2, 3, 4


try :
print('{ :.3f}'.format(sin(x)/x), end=" ")
except ZeroDivisionError : # toujours fournir un type d'exception
print(1.0, end=" ") # gère l'exception en 0
# -0.189 0.047 0.455 0.841 1.0 0.841 0.455 0.047 -0.189

Toutes les exceptions levées par Python appartiennent à un ensemble d’exceptions nommé Exception.
Cette famille offre une vingtaine d’exceptions standard 1.

Syntaxe complète d’une exception :

try :
... # séquence normale d'exécution
except <exception_1> as e1 :
... # traitement de l'exception 1
except <exception_2> as e2 :
... # traitement de l'exception 2
...

1. Citons quelques exemplaires : AritmeticError, ZeroDivisionError, IndexError, KeyError, AttributeError, IOError, ImportEr-ror, NameError,
SyntaxError, TypeError. . .
22 Le contrôle du flux d’instructions

else :
... # clause exécutée en l'absence d'erreur
finally :
... # clause toujours exécutée

L’instruction raise permet de lever volontairement une exception :

x = 2
if not(0 <= x <= 1) :
raise ValueError("x n'est pas dans [0 .. 1]")

Remarque
✓ raise, sans valeur, dans un bloc except, permet de ne pas bloquer une exception et de la
propager.
CHAPITRE 3

Les conteneurs standard

Le chapitre 1 a présenté les types de données simples, mais Python offre


beaucoup plus : les conteneurs.
De façon générale, un conteneur est un objet composite destiné à contenir
d’autres objets. Ce chapitre détaille les séquences, les tableaux associatifs, les
ensembles et les fichiers textuels.

4.1 Les séquences


4.1.1 Q’est-ce qu’une séquence ?
Définition
Une séquence est un conteneur ordonné d’éléments indexés par des entiers indiquant leur
position dans le conteneur.
Python dispose de trois types prédéfinis de séquences :
• les chaînes (vues précédemment) ;
• les listes ;
• les tuples 1.

4.2 Les listes


4.2.1 Définition, syntaxe et exemples
Définition
Une liste est une collection ordonnée et modifiable d’éléments éventuellement hétérogènes.
Syntaxe
Éléments séparés par des virgules, et entourés de crochets.
couleurs = ['trèfle', 'carreau', 'coeur', 'pique']
print(couleurs) # ['trèfle', 'carreau', 'coeur', 'pique']
couleurs[1] = 14
print(couleurs) # ['trèfle', 14, 'coeur', 'pique']
list1 = ['a', 'b']

1. « tuple » n’est pas vraiment un anglicisme, mais plutôt un néologisme informatique.


24 Les conteneurs standard

list2 = [4, 2.718]


list3 = [list1, list2] # liste de listes
print(list3) # [['a', 'b'], [4, 2.718]]

4.2.2 Initialisations et tests


Utilisation de la répétition, de l’itérateur d’entiers range() et de l’opérateur d’appartenance (in) :

>>> truc = []
>>> machin = [0.0] * 3
>>> truc
[]
>>> machin
[0.0, 0.0, 0.0]
>>>
>>> liste_1 = list(range(4))
>>> liste_1
[0, 1, 2, 3]
>>> liste_2 = list(range(4, 8))
>>> liste_2
[4, 5, 6, 7]
>>> liste_3 = list(range(2, 9, 2))
>>> liste_3
[2, 4, 6, 8]
>>>
>>> 2 in liste_1, 8 in liste_2, 6 in liste_3
(True, False, True)

4.2.3 Méthodes
Quelques méthodes de modification des listes :

>>> nombres = [17, 38, 10, 25, 72]


>>> [Link]()
>>> nombres
[10, 17, 25, 38, 72]
>>> [Link](12)
>>> [Link]()
>>> [Link](38)
>>> nombres
[12, 72, 25, 17, 10]
>>> [Link](17)
3
>>> nombres[0] = 11
>>> nombres[1:3] = [14, 17, 2]
>>> [Link]()
10
>>> nombres
[11, 14, 17, 2, 17]
>>> [Link](17)
2
>>> [Link]([1, 2, 3])
>>> nombres
[11, 14, 17, 2, 17, 1, 2, 3]

4.2.4 Manipulation des « tranches » (ou sous-chaînes)


Syntaxe
Si on veut supprimer, remplacer ou insérer plusieurs éléments d’une liste, il faut indiquer une tranche(cf.
2.7.7) dans le membre de gauche d’une affectation et fournir une liste dans le membre de droite.

>>> mots = ['jambon', 'sel', 'miel', 'confiture', 'beurre']


>>> mots[2:4] = [] # effacement par affectation d'une liste vide
>>> mots
['jambon', 'sel', 'beurre']
>>> mots[1:3] = ['salade']
>>> mots
['jambon', 'salade']
25

>>> mots[1:] = ['mayonnaise', 'poulet', 'tomate']


>>> mots
['jambon', 'mayonnaise', 'poulet', 'tomate']
>>> mots[2:2] = ['miel'] # insertion en 3è position
>>> mots
['jambon', 'mayonnaise', 'miel', 'poulet', 'tomate']

4.2.5 Des séquences de séquences


Les séquences, comme du reste les autres conteneurs, peuvent être
imbriqués. Par exemple :
>>> liste_1 = [1, 2, 3]
>>> listes = [liste_1, [4, 5], "abcd"]
>>>
>>> for liste in listes :
... for elem in liste :
... print(elem)
... print()
...
1

a
b
c
d

4.3 Les tuples


Définition
Un tuple est une collection ordonnée et non modifiable d’éléments éventuellement hétérogènes.
Syntaxe
Éléments séparés par des virgules, et entourés de parenthèses.
mon_tuple = ('a', 2, [1, 3])

• L’indexage des tuples s’utilisent comme celui des listes ;


• le parcours des tuples est plus rapide que celui des listes ;
• ils sont utiles pour définir des constantes.
Attention

🖂 Comme les chaînes de caractères, les tuples ne sont pas modifiables !

4.4 Retour sur les références


Nous avons déjà vu que l’opération d’affectation, apparemment innocente, est une réelle
difficulté de Python.

i = 1
msg = "Quoi de neuf ?"
e = 2.718

Dans l’exemple ci-dessus, les affectations réalisent plusieurs opérations :


• création en mémoire d’un objet du type approprié (membre de droite) ;
• stockage de la donnée dans l’objet créé ;
• création d’un nom de variable (membre de gauche) ;
• association de ce nom de variable avec l’objet contenant la valeur.
26 Les conteneurs standard

Une conséquence de ce mécanisme est que, si un objet modifiable est affecté à plusieurs
variables, tout changement de l’objet via une variable sera visible sur tous les autres :

fable = ["Je", "plie", "mais", "ne", "romps", "point"]


phrase = fable

phrase[4] = "casse"

print(fable) # ['Je', 'plie', 'mais', 'ne', 'casse', 'point']

Si on veut pouvoir effectuer des modifications séparées, il faut affecter l’autre variable par une
copie distincte de l’objet, soit en créant une tranche complète des séquences dans les cas simples,
soit en utilisant le module copy dans les cas les plus généraux (autres conteneurs). Dans les rares
occasions où l’on veut aussi que chaque élément et attribut de l’objet soit copié séparément et
de façon récursive, on emploie la fonction [Link]():
>>> import copy
>>> a = [1, 2, 3]
>>> b = a # une référence
>>> [Link](4)
>>> a
[1, 2, 3, 4]
>>> c = a[:] # une copie simple
>>> [Link](5)
>>> c
[1, 2, 3, 4, 5]
>>> d = [Link](a) # une copie de "surface"
>>> [Link](6)
>>> d
[1, 2, 3, 4, 6]
>>> a
[1, 2, 3, 4]
>>> d1 = {'a' : [1, 2], 'b' : [3, 4]}
>>> d2 = {'c' : (1, 2, 3), 'c' : (4, 5, 6)}
>>> liste_de_dicos = [d1, d2]
>>> nouvelle_liste_de_dicos = [Link](liste_de_dicos) # copie "profonde" (ou "récursive")
>>> nouvelle_liste_de_dicos
[{'a' : [1, 2], 'b' : [3, 4]}, {'c' : (4, 5, 6)}]

4.4.1 Complément graphique sur l’assignation


• Assignation augmentée d’un objet non modifiable (cas d’un entier : ☞ Fig. 4.1). On a
représenté l’étape de l’addition intermédiaire.

(a) Assignation d’un entier (b) Addition intermédiaire

(c) Assignation augmentée

Figure 4.1 – Assignation augmentée d’un objet non modifiable.

• Assignation augmentée d’un objet modifiable (cas d’une liste : ☞ Fig. 4.2). On a
représenté l’étape de la création de la liste intermédiaire.
27

(a) Assignation d’une liste (b) Création intermédiaire en mémoire

(c) Assignation augmentée

Figure 4.2 – Assignation augmentée d’un objet modifiable.

4.5 Les tableaux associatifs


4.5.1 Les types tableaux associatifs
Définition
Un tableau associatif est un type de données permettant de stocker des couples cle : valeur,
avec un accès très rapide à la valeur à partir de la clé, la clé ne pouvant être présente qu’une
seule fois dans le tableau.
Il possède les caractéristiques suivantes :
• l’opérateur d’appartenance d’une clé (in) ;
• la fonction taille (len()) donnant le nombre de couples stockés ;
• il est itérable (on peut le parcourir) mais n’est pas ordonné.

Python propose le type standard dict.

4.5.2 Les dictionnaires (dict)


Syntaxe
Collection de couples cle : valeur entourée d’accolades.

Les dictionnaires constituent un type composite mais ils n’appartiennent pas aux séquences.
Comme les listes, les dictionnaires sont modifiables, mais les couples enregistrés n’occupent pas un
ordre immuable, leur emplacement est géré par un algorithme spécifique.
Une clé pourra être alphabétique, numérique.. . en fait tout type hachable (donc liste et
dictionnaire exclus). Les valeurs pourront être de tout type sans exclusion.

Exemples de création

>>> d1 = {} # dictionnaire vide. Autre notation : d1 = dict()


>>> d1["nom"] = 3
>>> d1["taille"] = 176
>>> d1
{'nom' : 3, 'taille' : 176}
>>>
>>> d2 = {"nom" : 3, "taille" : 176} # définition en extension
>>> d2
{'nom' : 3, 'taille' : 176}
>>>
>>> d3 = {x : x**2 for x in (2, 4, 6)} # définition en compréhension
>>> d3
{2: 4, 4: 16, 6: 36}
>>>
>>> d4 = dict(nom=3, taille=176) # utilisation de paramètres nommés
>>> d4
28 Les conteneurs standard

{'taille' : 176, 'nom' : 3}


>>>
>>> d5 = dict([("nom", 3), ("taille", 176)]) # utilisation d'une liste de couples clés/valeurs
>>> d5
{'nom' : 3, 'taille' : 176}

Méthodes
Quelques méthodes applicables aux dictionnaires :
>>> tel = {'jack' : 4098, 'sape' : 4139}
>>> tel['guido'] = 4127
>>> tel
{'sape' : 4139, 'jack' : 4098, 'guido' : 4127}
>>> tel['jack']
4098
>>> del tel['sape']
>>> tel['irv'] = 4127
>>> tel
{'jack' : 4098, 'irv' : 4127, 'guido' : 4127}
>>>
>>> [Link]()
['jack', 'irv', 'guido']
>>> sorted([Link]())
['guido', 'irv', 'jack']
>>> sorted([Link]())
[4098, 4127, 4127]
>>> 'guido' in tel, 'jack' not in tel
(True, False)

4.6 Les ensembles (set)


Définition
Un ensemble est une collection itérable non ordonnée d’éléments hachables uniques.
Donc un set est la transposition informatique de la notion d’ensemble mathématique.
En Python, il existe deux types d’ensemble, les ensembles modifiables : set et les ensembles
non modifiables : frozenset. On retrouve ici les mêmes différences qu’entre les listes et les
tuples.

Figure 4.3 – Opérations sur les ensembles

>>> X = set('abcd')
>>> Y = set('sbds')
>>> X
set(['a', 'c', 'b', 'd'])
>>> Y
set(['s', 'b', 'd'])
>>> 'c' in X
True
>>> 'a' in Y
False
>>> X - Y
set(['a', 'c'])
>>> Y - X
set(['s'])
>>> X | Y
29

set(['a', 'c', 'b', 'd', 's'])


>>> X & Y
set(['b', 'd'])

4.7 Les fichiers textuels


4.7.1 Introduction
On rappelle que l’ordinateur n’exécute que les programmes présents dans sa mémoire volatile
(la RAM). Pour conserver durablement des informations, il faut utiliser une mémoire permanente
comme par exemple le disque dur, la clé USB, le DVD,.. . sur lesquels le système d’exploitation
organise les données sous la forme de fichiers.

Comme la plupart des langages, Python utilise classiquement la notion de fichier.

Nous limiterons nos exemples aux fichiers textuels (lisibles par un éditeur), mais signalons
que les fichiers stockés en codage binaire sont plus compacts et plus rapides à gérer (utiles pour
les grands volumes de données).

4.7.2 Gestion des fichiers


Ouverture et fermeture des fichiers
Principaux modes d’ouverture des fichiers textuels :

f1 = open("monFichier_1", "r", encoding='utf8') # "r" mode lecture


f2 = open("monFichier_2", "w", encoding='utf8') # "w" mode écriture
f3 = open("monFichier_3", "a", encoding='utf8') # "a" mode ajout

Python utilise les fichiers en mode texte par défaut (mode t). Pour les fichiers binaires, il faut
préciser le mode b.
Le paramètre optionnel encoding assure les conversions entre les types byte, stocké dans
le fichier sur le disque, et le type str, manipulé lors des lectures et écritures.
Les encodages les plus fréquents sont 'utf8' (c’est l’encodage à privilégier en Python 3),
'latin1', 'ascii'. . .
Tant que le fichier n’est pas fermé 1, son contenu n’est pas garanti sur le

disque. Une seule méthode de fermeture :

[Link]()

Écriture séquentielle
Le fichier sur disque est considéré comme une séquence de caractères qui sont ajoutés à la
suite, au fur et à mesure que l’on écrit dans le fichier.
Méthodes d’écriture :

f = open("[Link]", "w")
s = 'toto\n'
[Link](s) # écrit la chaîne s dans f
l = ['a', 'b', 'c']
[Link](l) # écrit les chaînes de la liste l dans f
[Link]()

f2 = open("[Link]", "w")
print("abcd", file=f2) # utilisation de l'option file
[Link]()

1. ou bien flushé par un appel à la méthode flush().


30 Les conteneurs standard

Lecture séquentielle
En lecture, la séquence de caractères qui constitue le fichier est parcourue en commençant au
début du fichier et en avançant au fur et à mesure des lectures.
Méthodes de lecture :

f = open("[Link]", "r")
s = [Link]() # lit tout le fichier --> chaîne
s = [Link](3) # lit au plus n octets --> chaîne
s = [Link]() # lit la ligne suivante --> chaîne
s = [Link]() # lit tout le fichier --> liste de chaînes
[Link]()

# Affichage des lignes d'un fichier une à une


f = open("[Link]") # mode "r" par défaut
for ligne in f :
print(ligne)
[Link]()

4.8 Itérer sur les conteneurs


Les techniques suivantes sont classiques et très utiles.

Obtenir clés et valeurs en bouclant sur un dictionnaire :


knights = {"Gallahad" : "the pure", "Robin" : "the brave"}
for k, v in [Link]() :
print(k, v)
# Gallahad the pure
# Robin the brave

Obtenir indice et élément en bouclant sur une liste :


for i, v in enumerate(["tic", "tac", "toe"]) :
print(i, v, end=" ", sep="->") # 0->tic 1->tac 2->toe

Boucler sur deux séquences (ou plus) appariées :


question = ["name", "quest", "favorite color"]
answers = ["Lancelot", "the Holy Grail", "blue"]
for q, a in zip(question, answers) :
print("What is your {} ? It is {}.".format(q, a))

# What is your quest ? It is the Holy Grail.


# What is your favorite color ? It is blue.

Obtenir une séquence inversée (la séquence initiale est inchangée) :


for i in reversed(range(1, 10, 2)) :
print(i, end=" ") # 9 7 5 3 1

Obtenir une séquence triée à éléments uniques (la séquence initiale est inchangée) :
basket = ["apple", "orange", "apple", "pear", "orange", "banana"]
for f in sorted(set(basket)) :
print(f, end=" ") # apple banana orange pear

4.9 L’affichage formaté


La méthode format() permet de contrôler finement la création de chaînes formatées. On
l’utilisera pour un affichage via print(), pour un enregistrement via [Link](), ou dans d’autres cas.
Remplacements simples :
print("{} {} {}".format("zéro", "un", "deux")) # zéro un deux

# formatage d'une chaîne pour usages ultérieurs


chain = "{2} {0} {1}".format("zéro", "un", "deux")

print(chain) # affichage : deux zéro un


31

with open("[Link]", "w", encoding="utf8") as f :


[Link](chain) # enregistrement dans un fichier

print("Je m'appelle {}".format("Bob")) # Je m'appelle Bob


print("Je m'appelle {{{}}}".format("Bob")) # Je m'appelle {Bob}
print("{}".format("-"*10)) # ----------

Remplacements avec champs nommés :


a, b = 5, 3
print("The story of {c} and {d}".format(c=a+b, d=a-b)) # The story of 8 and 2

Formatages à l’aide de liste :


stock = ['papier', 'enveloppe', 'chemise', 'encre', 'buvard']
print("Nous avons de l'{0[3]} et du {0[0]} en stock\n".format(stock)) # Nous avons de l'encre et du papier en stock

print("My name is {0[name]}".format(dict(name='Fred'))) # My name is Fred

d = dict(poids = 12000, animal = 'éléphant')


print("L'{0[animal]} pèse {0[poids]} kg\n".format(d)) # L'éléphant pèse 12000 kg

Formatages à l’aide de dictionnaire :

Remplacement avec attributs nommés :


import math
import sys

>>> print("{0!s} {0!r}".format("texte\n"))


print("[Link] = {.pi}, epsilon = {.float_info.epsilon}".format(math, sys))
texte
# [Link] = 3.14159265359, epsilon = 2.22044604925e-16
'texte\n'

Conversions textuelles, str() et repr()1 :

Formatages numériques :
s = "int :{0:d} ; hex : {0:x} ; oct : {0:o} ; bin : {0:b}".format(42)
print(s) # int :42; hex : 2a ; oct : 52; bin : 101010
s = "int :{0:d} ; hex : {0:#x} ; oct : {0:#o} ; bin : {0:#b}".format(42)
print(s) # int :42; hex : 0x2a ; oct : 0o52 ; bin : 0b101010

n = 100
pi = 3.1415926535897931
k = -54

print("{ :.4e}".format(pi)) # 3.1416e+00


print("{ :g}".format(pi)) # 3.14159
print("{ :.2%}".format(n/(47*pi))) # 67.73%

msg = "Résultat sur { :d} échantillons : { :.2f}".format(n, pi)


print(msg) # Résultat sur 100 échantillons : 3.14

msg = "{[Link]} et {[Link]} sont les composantes du complexe {0}".format(3-5j)


print(msg) # 3.0 et -5.0 sont les composantes du complexe (3-5j)

print("{ :+d} { :+d}".format(n, k)) # +100 -54 (on force l'affichage du signe)

print("{ :,}".format(1234567890.123)) # 1,234,567,890.12

Formatages divers : cf. fig. 4.4, p. 32

1. str() est un affichage orienté utilisateur alors que repr() est une représentation littérale.
32 Les conteneurs standard

Figure 4.4 – Formatages divers


CHAPITRE 5

Fonctions et espaces de noms

Les fonctions sont les éléments structurants de base de tout langage procédural.

Elles offrent différents avantages :


Évite la répétition : on peut « factoriser » une portion de code qui se répète lors de
l’exécution en séquence d’un script ;
Met en relief les données et les résultats : entrées et sorties de la fonction ;
Permet la réutilisation : mécanisme de l’import ;
Décompose une tâche complexe en tâches plus simples : conception de l’application.

Ces avantages sont illustrés sur la figure 5.1 qui utilise entre autres la notion d’import,
mé- canisme très simple qui permet de réutiliser des fichiers de fonctions, souvent
appelés modules ou bibliothèques.

5.1 Définition et syntaxe


Définition
Une fonction est un ensemble d’instructions regroupées sous un nom et s’exécutant à la demande.
On doit définir une fonction à chaque fois qu’un bloc d’instructions se trouve à plusieurs
reprises dans le code ; il s’agit d’une « mise en facteur commun ».
Syntaxe
La définition d’une fonction est composée :
• du mot clé def suivi de l’identificateur de la fonction, de parenthèses entourant les paramètres de la
fonction séparés par des virgules, et du caractère « deux points » qui termine toujours
une instruction composée ;
• d’une chaîne de documentation indentée comme le corps de la fonction ;
• du bloc d’instructions indenté par rapport à la ligne de définition, et qui constitue le
corps de la fonction.
Le bloc d’instructions est obligatoire. S’il est vide, on emploie l’instruction pass. La
documentation (facultative) est fortement conseillée.
def afficheAddMul(a, b) :
"""Calcule et affiche la somme et le produit de a et b."""
somme = a + b
produit = a * b
print("La somme de", a, " et", b, " est", somme, " et le produit, produit)
34 Fonctions et espaces de noms

(a) Évite la duplication de code.

(b) Met en relief entrées et sorties.

(c) L’import permet la réutilisation.

(d) Améliore la conception.

Figure 5.1 – Les avantages de l’utilisation des fonctions


35

5.2 Passage des arguments


5.2.1 Mécanisme général
Remarque
✓ Passage par affectation : chaque argument de la définition de la fonction correspond, dans
l’ordre, à un paramètre de l’appel. La correspondance se fait par affectation des paramètres aux
arguments.

Figure 5.2 – Passage des arguments par affectation des paramètres aux arguments.

5.2.2 Un ou plusieurs paramètres, pas de retour


Exemple sans l’instruction return, ce qu’on appelle souvent une procédure 1. Dans ce cas la
fonction renvoie implicitement la valeur None:
def table(base, debut, fin) :
"""Affiche la table de multiplication des <base> de <debut> à <fin>."""
n = debut
while n <= fin :
print(n, 'x', base, '=', n * base)
n += 1

# exemple d'appel :
table(7, 2, 8)
# 2 x 7 = 14 3 x 7 = 21 4 x 7 = 28 5 x 7 = 35 6 x 7 = 42 7 x 7 = 49 8 x 7 = 56

# autre exemple du même appel, mais en nommant les paramètres ;


table(base=7, debut=2, fin=8)

5.2.3 Un ou plusieurs paramètres, un ou plusieurs retours


Exemple avec utilisation d’un return unique :
from math import pi

def cube(x) :
"""Retourne le cube de largument."""
return x**3

def volumeSphere(r) :
"""Retourne le volume d'une sphère de rayon <r> ;"""
return 4.0 * pi * cube(r) / 3.0

# Saisie du rayon et affichage du volume


rayon = float(input('Rayon : '))
print("Volume de la sphère =", volumeSphere(rayon))

1. Une fonction vaut quelque chose, une procédure fait quelque chose.
36 Fonctions et espaces de noms

Exemple avec utilisation d’un return multiple :

import math

def surfaceVolumeSphere(r) :
surf = 4.0 * [Link] * r**2
vol = surf * r/3
return surf, vol

# programme principal
rayon = float(input('Rayon : '))
s, v = surfaceVolumeSphere(rayon)
print("Sphère de surface { :g} et de volume { :g}".format(s, v))

5.2.4 Passage d’une fonction en paramètre


Puisque en Python une variable peut référencer une fonction, on peut transmettre une
fonction comme paramètre :
>>> def f(x) :
... return 2*x+1
...
>>> def g(x) :
... return x//2
...
>>> def h(fonc, x) :
... return fonc(x)
...
>>>
>>> h(f, 3)

>>> h(g, 4)

5.2.5 Paramètres avec valeur par défaut


Il est possible de spécifier, lors de la déclaration, des valeurs par défaut à utiliser pour les
arguments. Cela permet, lors de l’appel, de ne pas avoir à spécifier les paramètres
correspondants.
Il est possible, en combinant les valeurs par défaut et le nommage des paramètres, de
n’indiquer à l’appel que les paramètres dont on désire modifier la valeur de l’argument. Il est par
contre nécessaire de regrouper tous les paramètres optionnels à la fin de la liste des paramètres.
>>> def accueil(nom, prenom, depart="MP", semestre="S2") :
... print(prenom, nom, "Département", depart, "semestre", semestre)
...
>>> accueil("Student", "Joe")
Joe Student Département MP semestre S2
>>> accueil("Student", "Eve", "Info")
Eve Student Département Info semestre S2
>>> accueil("Student", "Steph", semestre="S3")
Steph Student Département MP semestre S3

Attention
🖂 On utilise de préférence des valeurs par défaut non modifiables (int, float, str, bool, tuple) car
la modification d’un paramètre par un premier appel est visible les fois suivantes.
Si on a besoin d’une valeur par défaut qui soit modifiable (list, dict), on utilise la valeur
prédéfinie
None et on fait un test au début de la fonction :

def maFonction(liste=None) :
if liste is None :
liste = [1, 3]
5.3 Espace
s de noms 37

5.2.6 Nombre d’arguments arbitraire : passage d’un tuple de valeurs


Le passage d’un nombre arbitraire d’arguments est permis en utilisant la notation d’un argument
final
*nom. Les paramètres surnuméraires sont alors transmis sous la forme d’un tuple affecté à cet
argument (que l’on appelle généralement args).
def somme(*args) :
"""Renvoie la somme du tuple <args>."""
resultat = 0
for nombre in args :
resultat += nombre
return resultat

# Exemples d'appel :
print(somme(23)) # 23
print(somme(23, 42, 13)) # 78

Attention

🖂 Si la fonction possède plusieurs arguments, le tuple est en dernière position.


Réciproquement il est aussi possible de passer un tuple (en fait une séquence) à l’appel qui sera
dé- compressé en une liste de paramètres d’une fonction « classique » :
def somme(a, b, c) :
return a+b+c

# Exemple d'appel :
elements = (2, 4, 6)
print(somme(*elements)) # 12

5.2.7 Nombre d’arguments arbitraire : passage d’un dictionnaire


De la même façon, il est possible d’autoriser le passage d’un nombre arbitraire d’arguments
nommés en plus de ceux prévus lors de la définition en utilisant la notation d’un argument final
**nom. Les paramètres surnuméraires nommés sont alors transmis sous la forme d’un
dictionnaire affecté à cet argument (que l’on appelle généralement kwargs pour keyword
args).
Réciproquement il est aussi possible de passer un dictionnaire à l’appel d’une fonction, qui
sera dé- compressé et associé aux paramètres nommés de la fonction.
def unDict(**kwargs) :
return kwargs

# Exemples d'appels
## par des paramètres nommés :
print(unDict(a=23, b=42)) # {'a' : 23, 'b' : 42}

## en fournissant un dictionnaire :
mots = {'d' : 85, 'e' : 14, 'f' :9}
print(unDict(**mots)) # {'e' : 14, 'd' : 85, 'f' : 9}

Attention
🖂 Si la fonction possède plusieurs arguments, le dictionnaire est en toute dernière
position (après un éventuel tuple).

5.3 Espaces de noms


5.3.1 Portée des objets
Remarque
✓ Portée : les noms des objets sont créés lors de leur première affectation, mais ne sont
visibles que dans certaines régions de la mémoire.
On distingue :
La portée globale : celle du module ou du fichier script en cours. Un dictionnaire gère les objets globaux :
l’instruction globals()fournit un dictionnaire contenant les couples variable:valeur;
La portée locale : les objets internes aux fonctions sont locaux. Les objets globaux ne sont
pas modifi- ables dans les portées locales. L’instruction locals() fournit un dictionnaire
contenant les couples
variable:valeur.
38 Fonctions et espaces de noms

5.3.2 Résolution des noms : règle « LGI »


La recherche des noms est d’abord locale (L), puis globale (G), enfin interne (I) (☞ Fig. 5.3) :

F 5.3 – Règle LGI

Exemples de portée
Par défaut, toute variable utilisée dans le corps d’une fonction est locale à celle-ci. Si une
fonction a besoin de modifier certaines variables globales, la première instruction de cette
global <identificateurs>

fonction doit être :


Par exemple :
# x et fonc sont affectés dans le module : globaux

def fonc(y) : # y et z sont affectés dans fonc : locaux


global x # permet de modifier x ligne suivante
x += 2
z = x + y
return z

x = 99
print(fonc(1)) # 102
print(x) # 101

# x et fonc sont affectés dans le module : globaux

def fonc(y) : # y et z sont affectés dans fonc : locaux


# dans fonc : portée locale
z = x + y
return z

x = 99
print(fonc(1)) # 100
print(x) # 99

# x et fonc sont affectés dans le module : globaux

def fonc(y) : # y, x et z sont affectés dans fonc : locaux


x = 3 # ce nouvel x est local et masque le x global
z = x + y
return z

x = 99
print(fonc(1)) # 4
print(x) # 99
CHAPITRE 6

Modules et packages

Un programme Python est généralement composé de plusieurs fichiers sources,


appelés mod- ules.
S’ils sont correctement codés les modules doivent être indépendants les uns des
autres pour être réutilisés à la demande dans d’autres programmes.
Ce chapitre explique comment coder des modules et comment les importer dans
un autre. Nous verrons également la notion de package qui permet de grouper
plusieurs modules.

6.1 Modules
Définition
Module : fichier script Python permettant de définir des élements de programme réutilisables. Ce
mécanisme permet d’élaborer efficacement des bibliothèques de fonctions ou de classes.

Avantages des modules :


• réutilisation du code ;
• la documentation et les tests peuvent être intégrés au module ;
• réalisation de services ou de données partagés ;
• partition de l’espace de noms du système.

Remarque
✓ Lorsqu’on parle du module, on omet l’extention : le module machin est dans le fichier [Link] ou
[Link].

6.1.1 Import
L’instruction importcharge et exécute le module indiqué s’il n’est pas déjà chargé. L’ensemble des
défini- tions contenues dans ce module deviennent alors disponibles : variables globales,
fonctions, classes.
Suivant la syntaxe utilisée, on accède aux définitions du module de différentes façons :

• l’instruction import <nom_module> donne accès à l’ensemble des définitions du module


importé en utilisant le nom du module comme espace de nom.
>>> import tkinter
>>> print("Version de l'interface graphique tkinter :", [Link])
Version de l'interface graphique tkinter : 8.5
40 Modules et packages

• l’instruction from <nom_module> import nom1, nom2. . . donne accès directement à une
sélection choisie de noms définis dans le module.
>>> from math import pi, sin
>>> print("Valeur de Pi :", pi, "sinus(pi/4) :", sin(pi/4))
Valeur de Pi : 3.14159265359 sinus(pi/4) : 0.707106781187

Dans les deux cas, le module et ses définitions existent dans leur espace mémoire propre, et on
duplique simplement dans le module courant les noms que l’on a choisi, comme si on avait fait les
affectations :
>>> sin = [Link]
>>> pi = [Link]

Remarque
✓ Il est conseillé d’importer dans l’ordre :
• les modules de la bibliothèque standard ;
• les modules des bibliothèques tierces ;
• Les modules personnels.
Attention
🖂 Pour tout ce qui est fonction et classe, ainsi que pour les « constantes » (variables globales
définies et affectée une fois pour toute à une valeur), l’import direct du nom ne pose pas de
problème.
Par contre, pour les variables globales que l’on désire pouvoir modifier, il est préconisé de
passer systématiquement par l’espace de nom du module afin de s’assurer de l’existence de
cette variable en un unique exemplaire ayant la même valeur dans tout le programme.

6.1.2 Exemples
Notion d’« auto-test »
Le module principal est celui qui est donné en argument sur la ligne de commande ou qui est
lancé en premier lors de l’exécution d’un script. Son nom est contenu dans la variable globale
name . Sa valeur dépend du contexte de l’exécution.

Soit le module :
# je_me_nomme.py
print("Je me nomme :", name )

Premier contexte exécution sur la ligne de commande (ou dans un EDI), on obtient la valeur
prédéfinie
main :
$ python3 je_me_nomme.py
Je me nomme : main

Second contexte import de ce module (ce n’est donc plus le module principal ), on obtient
l’identificateur du module :
>>> import je_me_nomme
Je me nomme : je_me_nomme

Grâce à un test, on peut donc facilement savoir si le code est exécuté en tant que script
principal :

• on écrit un script de fonctions ou de classes (souvent appelé bibliothèque) et on termine le


fichier par un test, l’« auto-test », pour vérifier que l’on est dans le module principal. On en
profite pour vérifier tous les éléments de la bibliothèque ;
• quand on importe le script, le test inclus est faux et on se borne à utiliser la bibliothèque.

# cube_m.py

def cube(x) :
"""retourne le cube de <x>."""
return x**3

# auto-test ===================================================================
if name == " main " : # vrai car module principal
print("OK !") if cube(9) == 729 else "KO !"
6.2 Bibliothèque standard 41

Utilisation de ce module :
import cube_m

# programme principal =========================================================


for i in range(1, 4) :
print("cube de", i, "=", cube_m.cube(i))

"""
cube de 1 = 1
cube de 2 = 8
cube de 3 = 27
"""

Autre exemple :
def ok(message) :
"""
Retourne True si on saisie <Entrée>, <O>, <o>, <Y> ou <y>,
False dans tous les autres cas.
"""
s = raw_input(message + " (O/n) ? ")
return True if s == "" or s[0] in "OoYy" else False

# auto-test ===================================================================
if name == ' main ' :
import sys

while True :
if ok("Encore") :
print("Je continue")
else :
[Link]("Je m'arrête")

"""
Encore (O/n) ?
Je continue
Encore (O/n) ? o
Je continue
Encore (O/n) ? n
Je m'arrête
"""

6.2 Bibliothèque standard


6.2.1 La bibliothèque standard
On dit souvent que Python est livré « piles comprises » (ba eries included) tant sa bibliothèque
standard, riche de plus de 200 packages et modules, répond aux problèmes courants les plus
variés.
Ce survol présente quelques fonctionnalités utiles.

La gestion des aînes


Le module stringfournit des constantes comme ascii_lowercase, digits. . . ainsi que la classe Formatter
qui peut être spécialisée en sous-classes de formateurs de chaînes.
Le module textwrap est utilisé pour formater un texte : longueur de chaque ligne, contrôle de
l’inden- tation.
Le module struct permet de convertir des nombres, booléens et des chaînes en leur
représentation binaire afin de communiquer avec des bibliothèques de bas-niveau (souvent en
C).
Le module difflib permet la comparaison de séquences et fournit des sorties au format standard
« diff » ou en HTML.
Enfin on ne peut oublier le module re qui offre à Python la puissance des expressions régulières 1.
1. C’est tout un monde.. .
42 Modules et packages

Le module [Link]

Ce module fournit des objets compatibles avec l’interface des objets fichiers.
Exemple de gestion ligne à ligne d’un fichier ou d’une chaîne avec la même fonction scanner()
utilisant le même traitement dans les deux cas :

def scanner(objet_fichier, gestionnaire_ligne) :


for ligne in objet_fichier :
gestionnaire_ligne(ligne)

def premierMot(ligne) : print([Link]()[0])

fic = open("[Link]")
scanner(fic, premierMot)

import io
chaine = [Link]("un\ndeux xxx\ntrois\n")
scanner(chaine, premierMot)

La gestion de la ligne de commande


Pour gérer la ligne de commande, Python propose l’instruction [Link]. Il s’agit simplement d’une
liste contenant les arguments de la ligne de commande : argv[1], argv[2]. . . sachant que argv[0] est le
nom du script lui-même.

De plus, Python propose le module optparse:


from optparse import OptionParser
parser = OptionParser()
parser.add_option("-f", "--file", dest="filename",
help="write report to FILE", metavar="FILE")
parser.add_option("-q", "--quiet",
action="store_false", dest="verbose", default=True,
help="don't print status messages to stdout")

(options, args) = parser.parse_args()

Les lignes de commande :

python 6_025.py -h

ou
python 6_025.py --help

produisent la même documentation :

Usage : 6_025.py [options]

Options :
-h, --help show this help message and exit
-f FILE, --file=FILE write report to FILE
-q, --quiet don't print status messages to stdout

Bibliothèques mathématiques et types numériques


On rappelle que Python possède la bibliothèque math :
>>> import math
>>> [Link] / math.e
1.1557273497909217
>>> [Link](1e-5) - 1
1.0000050000069649e-05
>>> [Link](10)
2.302585092994046
>>> [Link](1024, 2)
10.0
>>> [Link]([Link]/4)
0.7071067811865476
>>> [Link](4.1/9.02)
6.2 Bibliothèque standard 43

0.4266274931268761
>>> [Link](3, 4)
5.0
>>> [Link](1)
57.29577951308232

Par ailleurs, Python propose en standard les modules fraction et decimal :


from fractions import Fraction
import decimal as d

print(Fraction(16, -10)) # -8/5


print(Fraction(123)) # 123
print(Fraction(' -3/7 ')) # -3/7
print(Fraction('-.125')) # -1/8
print(Fraction('7e-6')) # 7/1000000

[Link]().prec = 6
print([Link](1) / [Link](7)) # 0.142857
[Link]().prec = 18
print([Link](1) / [Link](7)) # 0.142857142857142857

En plus des bibliothèques math et cmath déjà vues, la bibliothèque random propose plusieurs
fonctions de nombres aléatoires.

La gestion du temps et des dates


Les modules calendar, time et datetime fournissent les fonctions courantes de gestion du temps et
des durées :

import calendar, datetime, time

moon_apollo11 = [Link](1969, 7, 20, 20, 17, 40)


print(moon_apollo11)
print([Link]([Link](0)))
# Thu Jan 01 00:00:00 1970 ("epoch" UNIX)

vendredi_precedent = [Link]()
un_jour = [Link](days=1)
while vendredi_precedent.weekday() != [Link] :
vendredi_precedent -= un_jour
print(vendredi_precedent.strftime("%A, %d-%b-%Y"))
# Friday, 09-Oct-2009

Algorithmes et types de données collection


Le module bisect fournit des fonctions de recherche de séquences triées. Le module array
propose un type semblable à la liste, mais plus rapide car de contenu homogène.
Le module heapqgère des tas dans lesquels l’élément d’index 0 est toujours le plus petit :
import heapq
import random

heap = []
import collections
for i in range(10) :
[Link](heap,
# description du type :[Link](2, 9))
Point = [Link]("Point", "x y z")
print(heap) # [2, 3, 5, 4, 6, 6, 7, 8, 7, 8]
# on instancie un point :
point = Point(1.2, 2.3, 3.4)
# on l'affiche :
print("point : [{}, {}, {}]"
.format(point.x, point.y, point.z)) # point : [1.2, 2.3, 3.4]

À l’instar des structures C, Python propose désormais, via le module collections, la notion de
type tuple nommé :
44 Modules et packages

Il est bien sûr possible d’avoir des tuples nommés


emboîtés. Le type defaultdict permet des utilisations
avancées :

from collections import defaultdict

s = [('y', 1), ('b', 2), ('y', 3), ('b', 4), ('r', 1)]
d = defaultdict(list)
for k, v in s :
d[k].append(v)
print([Link]())
# dict_items([('y', [1, 3]), ('r', [1]), ('b', [2, 4])])

s = 'mississippi'
d = defaultdict(int)
for k in s :
d[k] += 1
print([Link]())
# dict_items([('i', 4), ('p', 2), ('s', 4), ('m', 1)])

Et tant d’autres domaines.. .


Beaucoup d’autres sujets pourraient être explorés :
• accès au système ;
• utilitaires fichiers ;
• programmation réseau ;
• persistance ;
• les fichiers XML ;
• la compression ;
• ...

6.3 Bibliothèques tierces


6.3.1 Une grande diversité
Outre les modules intégrés à la distribution standard de Python, on trouve des bibliothèques
dans tous les domaines :
• scientifique ;
• bases de données ;
• tests fonctionnels et contrôle de qualité ;
• 3D ;
• ...

Le site [Link]/pypi ( e Python Package Index) recense des milliers de modules et


de pack- ages !

6.3.2 Un exemple : la bibliothèque Unum

Elle permet de calculer en tenant compte des unités du système SI (Système International
d’unités).
Voici un exemple de session interactive :

>>> from [Link] import *


>>> distance = 100*m
>>> temps = 9.683*s
>>> vitesse = distance / temps
>>> vitesse
10.327377878756584 [m/s]
>>> [Link](mile/h)
23.1017437978 [mile/h]
>>> acceleration = vitesse/temps
>>> acceleration
1.0665473385063085 [m/s2]
6.4 Paquets
Outre le module, un deuxième niveau d’organisation permet de structurer le code : les fichiers
Python peuvent être organisés en une arborescence de répertoires appelée paquet, en anglais
package.

Définition
Un package est un module contenant d’autres modules. Les modules d’un package peuvent être
des sous-packages, ce qui donne une structure arborescente.

Chaque répertoire du paquet doit posséder un fichier init qui peut soit être vide soit
contenir du code d’initialisation.
CHAPITRE 7

La Programmation Orientée Objet

La Programmation Orientée Objet :


• la POO permet de mieux modéliser la réalité en concevant des modèles d’objets, les classes.
• Ces classes permettent de construire des objets interactifs entre eux et avec le
monde ex- térieur.
• Les objets sont créés indépendamment les uns des autres, grâce à l’encapsulation,
mécanisme qui permet d’embarquer leurs propriétés.
• Les classes permettent d’éviter au maximum l’emploi des variables globales.
• Enfin les classes offrent un moyen économique et puissant de construire de
nouveaux objets à partir d’objets préexistants.

7.1 Terminologie
Le vocabulaire de base de la POO

Une classe est équivalente à un nouveau type de données. On connaît déjà par exemple les classes
list
ou str et les nombreuses méthodes permettant de les manipuler, par exemple :
• [3, 5, 1].sort()
• "casse".upper()

Un objet ou une instance est un exemplaire particulier d’une classe. Par exemple [3, 5, 1]
est une instance de la classe listet "casse"est une instance de la classe str.

Les objets ont généralement deux sortes d’attributs : les données nommées simplement
attributs et les fonctions applicables appelées méthodes.
Par exemple un objet de la classe complexpossède :
• deux attributs : imaget real;
• plusieurs méthodes, comme conjugate(), abs(). . .

La plupart des classes encapsulent à la fois les données et les méthodes applicables aux
objets. Par exemple un objet strcontient une chaîne de caractères Unicode (les données) et de
nombreuses méthodes.

On peut définir un objet comme une capsule contenant des attributs et des méthodes :
, ,
objet = attributs + méthodes
z r
48 La Programmation Orientée Objet

7.1.1 Notations UML de base


Remarque
✓ L’UML (Unified Modeling Language) est un langage graphique très répandu de conception des
systèmes d’information.
UML propose une grande variété de diagrammes (classes, objets, états, activités etc.).
En première approche, le diagramme de classes est le plus utile pour concevoir les classes et
leurs relations.

(a) Une classe et une note (b) Un héritage

F 7.1 – Diagrammes de classe.

7.2 Classes et instanciation d’objets


7.2.1 L’instruction class

Cette instruction permet d’introduire la définition d’une nouvelle classe (c’est-à-dire d’un
nouveau type de données).
Syntaxe

B class est une instruction composée. Elle comprend un en-tête (avec docstring) + corps indenté

:
>>> class C :
... """Documentation de la clesse C."""
... x = 23

Dans cet exemple, C est le nom de la classe (qui commence conventionnellement par une
majuscule), et xest un a ribut de classe, local à C.

7.2.2 L’instanciation et ses attributs


• Les classes sont des fabriques d’objets : on construit d’abord l’usine avant de produire des
objets !
• On instancie un objet (c’est-à-dire qu’on le produit à partir de l’usine) en appelant le
nom de sa classe comme s’il s’agissait d’une fonction :
>>> class C :
... """Documentation de la clesse C."""
... x = 23 # attibut de classe
...
>>> a = C() # a est un objet de la classe C (ou une instance)
>>> a.x # affiche la valeur de l'attribut de l'instance a
23
>>> a.x = 12 # modifie son attribut d'instance (attention...)
>>> a.x
12
>>> C.x # mais l'attribut de classe est inchangé
23
>>> C.z = 6 # z : nouvel attribut de classe
>>> a.y = 44 # y : nouvel attribut de l'instance a
>>> b = C() # b est un autre objet de la classe C (une autre instance)
>>> b.x # b connaît bien son attribut de classe, mais...
23
>>> b.y # ... b n'a pas d'attribut y !
7.2 Classes et instanciation d’objets 49

Traceback (most recent call last) :


File "<stdin>", line 1, in <module>
AttributeError : C instance has no attribute 'y'

Remarque
✓ En Python (car c’est un langage dynamique comme Ruby, contrairement à C++ ou Java) il
est possible d’ajouter de nouveaux attributs d’instance (ici le a.y = 44) ou même de nouveaux
attributs de classe (ici
C.z = 6).

Définition
Une variable définie au niveau d’une classe (comme x dans la classe C) est appelé attribut de
classe et est partagée par tous les objets instances de cette classe.

Définition
Une variable définie au niveau d’un objet (comme y dans l’objet a) est appelée attribut d’instance
et est liée uniquement à l’objet pour lequel elle est définie.

7.2.3 Retour sur les espaces de noms


On a déjà vu les espaces de noms 1 locaux (lors de l’appel d’une fonction), globaux (liés aux
modules) et internes (fonctions standard), ainsi que la règle « Local Global Interne » (cf. section
5.3, p. 38) qui définit dans quel ordre ces espaces sont parcourus pour résoudre un nom.
Les classes ainsi que les objets instances définissent de nouveaux espaces de noms, et il y a
là aussi des règles pour résoudre les noms :

• les classes peuvent utiliser les variables définies au niveau principal mais elles ne peuvent
pas les modifier ;
• les instances peuvent utiliser les variables définies au niveau de la classe mais elles ne
peuvent pas les modifier (pour cela elles sont obligées de passer par l’espace de noms de
la classe, par exemple
C.x = 3).

Re er e des noms
• Noms non qualifiés (exemple dimension) l’affectation crée ou change le nom dans la portée
locale courante. Ils sont cherchés suivant la règle LGI.
• Noms qualifiés (exemple [Link]) l’affectation crée ou modifie l’attribut dans l’espace
de noms de l’objet. Un attribut est cherché dans l’objet, puis dans toutes les classes dont
l’objet dépend (mais pas dans les modules).

L’exemple suivant affiche le dictionnaire lié à la classe C puis la liste des attributs liés à une
instance de C:
>>> v = 5
>>> class C :
... x = v + 3 # utilisation d'une variable globale dans la définition de classe
... y = x + 1 # recherche dans l'espace de noms de la classe lors de la définition
...
>>> a = C()
>>> a.x # utilisation sans modification de la variable de classe en passant par l'objet
8
>>> a.x = 2 # création d'une variable d'instance pour l'objet a
>>> a.x
2
>>> C.x # la variable de classe n'est pas modifiée
8
>>> C.x = -1 # on modifie la variable de classe en passant par l'espace de noms de la classe
>>> C.x
-1

>>> class C :
... x = 20
...
>>> C. dict

1. Les espaces de noms sont implémentés par des dictionnaires.


50 La Programmation Orientée Objet

{'x' : 20, ' module ' : ' main ', ' doc ' : None}
>>> a = C()
>>> dir(a)
[' doc ', ' module ', 'x']

7.3 Méthodes
Syntaxe
Une méthode s’écrit comme une fonction du corps de la classe avec un premier paramètre self
obli- gatoire, où selfreprésente l’objet sur lequel la méthode sera appliquée.
Autrement dit self est la référence d’instance.

>>> class C : # x et y : attributs de classe


... x = 23
... y = x + 5
... def affiche(self) : # méthode affiche()
... self.z = 42 # attribut d'instance
... print(C.y) # dans une méthode, on qualifie un attribut de classe,
... print(self.z) # mais pas un attribut d'instance
...
>>> obj = C() # instanciation de l'objet ob
>>> [Link]()
28
42

7.4 Méthodes spéciales


Beaucoup de classes offrent des caractéristiques supplémentaires comme par exemple la
concaténation des chaînes en utilisant simplement l’opérateur +. Ceci est obtenu grâce aux
méthodes spéciales. Par ex- emple l’opérateur +est utilisable car la classe des chaînes a redéfini
la méthode spéciale add ().

Syntaxe

B Ces méthodes portent des noms pré-définis, précédés et suivis de deux caractères de

soulignement.
Elles servent :
• à initialiser l’objet instancié ;
• à modifier son affichage ;
• à surcharger ses opérateurs ;
• ...

7.4.1 L’initialisateur
Lors de l’instanciation d’un objet, la structure de base de l’objet est créée en mémoire, et la
méthode
init est automatiquement appelée pour initialiser l’objet. C’est typiquement dans cette méthode
spé- ciale que sont créés les attributs d’instance avec leur valeur initiale.

>>> class C :
... def init (self, n) :
... self.x = n # initialisation de l'attribut d'instance x
...
>>> une_instance = C(42) # paramètre obligatoire, affecté à n
>>> une_instance.x
42

C’est une procédure automatiquement invoquée lors de l’instanciation : elle ne retourne


aucune valeur.

7.4.2 Surcharge des opérateurs


La surcharge permet à un opérateur de posséder un sens différent suivant le type de ses
opérandes. Par exemple, l’opérateur + permet :
7.5 Héritage et polymorphisme 51

x = 7 + 9 # addition entière
s = 'ab' + 'cd' # concaténation

Python possède des méthodes de surcharge pour :


• tous les types ( call , str , . . .) ;
• les nombres ( add , div , . . .) ;
• les séquences ( len , iter , . . .).
Soient deux instances, obj1 et obj2, les méthodes spéciales suivantes permettent
d’effectuer les opéra- tions arithmétiques courantes 1 :

Nom Méthode spéciale Utilisation


opposé neg -obj1
addition add obj1 + obj2
soustraction sub obj1 - obj2
multiplication mul obj1 * obj2
division div obj1 / obj2
division entière floordiv obj1 // obj2

7.4.3 Exemple de sur arge


Dans l’exemple nous surchargeons l’opérateur d’addition pour le type Vecteur2D.
Nous surchargeons également la méthode spéciale str utilisée pour l’affichage 2 par print().
>>> class Vecteur2D :
... def init (self, x0, y0) :
... self.x = x0
... self.y = y0
... def add (self, second) : # addition vectorielle
... return Vecteur2D(self.x + second.x, self.y + second.y)
... def str (self) : # affichage d'un Vecteur2D
... return "Vecteur({ :g}, { :g})".format(self.x, self.y)
...
>>>
>>> v1 = Vecteur2D(1.2, 2.3)
>>> v2 = Vecteur2D(3.4, 4.5)
>>>
>>> print(v1 + v2)
Vecteur(4.6, 6.8)

7.5 Héritage et polymorphisme


Un avantage décisif de la POO est qu’une classe Python peut toujours être spécialisée en une
classe fille qui hérite alors de tous les attributs (données et méthodes) de sa super classe. Comme
tous les attributs peuvent être redéfinis, une méthode de la classe fille et de la classe mère peut
posséder le même nom mais effectuer des traitements différents (surcharge) et l’objet s’adaptera
dynamiquement, dès l’instanciation. En proposant d’utiliser un même nom de méthode pour
plusieurs types d’objets différents, le polymor- phisme permet une programmation beaucoup plus
générique. Le développeur n’a pas à savoir, lorsqu’il programme une méthode, le type précis de
l’objet sur lequel la méthode va s’appliquer. Il lui suffit de savoir que cet objet implémentera la
méthode.

7.5.1 Héritage et polymorphisme


Définition
L’héritage est le mécanisme qui permet de se servir d’une classe préexistante pour en créer une
nou- velle qui possédera des fonctionnalités supplémentaires ou différentes.

Définition
Le polymorphisme par dérivation est la faculté pour deux méthodes (ou plus) portant le même nom
1. Pour plus de détails, consulter la documentation de référence du langage Python ( e Python language reference) section
3,
Data model, sous-section 3.3, Special method names.
2. Rappelons qu’il existe deux façons d’afficher un résultat : repr() et str(). La première est « pour la machine », la seconde
« pour l’utilisateur ».
52 La Programmation Orientée Objet

mais appartenant à des classes héritées distinctes d’effectuer un travail différent. Cette
propriété est ac- quise par la technique de la surcharge.

7.5.2 Exemple d’héritage et de polymorphisme


Dans l’exemple suivant, la classe QuadrupedeDebout hérite de la classe mère Quadrupede, et la
méthode
piedsAuContactDuSol() est polymorphe :

>>> class Quadrupede :


... def piedsAuContactDuSol(self) :
... return 4
...
>>> class QuadrupedeDebout(Quadrupede) :
... def piedsAuContactDuSol(self) :
... return 2
...
>>> q1 = Quadrupede()
>>> [Link]()
4
>>> q2 = QuadrupedeDebout()
>>> [Link]()
2

7.6 Notion de conception orientée objet


Suivant les relations que l’on va établir entre les objets de notre application, on peut concevoir
nos classes de deux façons possibles en utilisant l’association ou la dérivation.

Bien sûr, ces deux conceptions peuvent cohabiter, et c’est souvent le cas !

7.6.1 Association
Définition
b Une association représente un lien unissant les instances de classe. Elle repose sur la relation
« a-un » ou « utilise-un ».

F 7.2 – Une association peut être étiquetée et avoir des multiplicités.

L’implémentation Python utilisée est généralement l’intégration d’autres objets dans le


constructeur de la classe conteneur.

class Point :
def init (self, x, y) :
[Link], [Link] = x, y

class Segment :
"""Classe conteneur utilisant la classe Point."""
def init (self, x1, y1, x2, y2) :
[Link] = Point(x1, y1)
[Link] = Point(x2, y2)

def str (self) :


return ("Segment : [({ :g}, { :g}), ({ :g}, { :g})]"
7.6 Notion de conception orientée objet 53

.format([Link], [Link],
[Link], [Link]))

s = Segment(1.0, 2.0, 3.0, 4.0)


print(s) # Segment : [(1, 2), (3, 4)]

Agrégation
Définition
b Une agrégation est une association non symétrique entre deux classes (l’agrégat et le composant).

F 7.3 – Une voiture est un tout qui contient un moteur.

Composition
Définition
b Une composition est un type particulier d’agrégation dans laquelle la vie des composants
est liée à celle de l’agrégat.

F 7.4 – On peut mêler les deux types d’association.

La disparition de l’agrégat Communeentraine le disparition des composants Serviceset Conseil_Municipal


alors que Villagen’en dépend pas.

7.6.2 Dérivation
Définition

b La dérivation décrit la création de sous-classes par spécialisation. Elle repose sur la relation « est-un
».

On utilise dans ce cas le mécanisme de l’héritage.


L’implémentation Python utilisée est généralement l’appel à l’initialisateur de la classe
parente dans l’initialisateur de la classe dérivée (utilisation de la fonction super()).

Dans l’exemple suivant, un Carre« est-un » Rectangleparticulier pour lequel on appelle


l’initialisateur de la classe mère avec les paramètres longueur=coteet largeur=cote.
54 La Programmation Orientée Objet

>>> class Rectangle :


... def init (self, longueur=30, largeur=15) :
... self.L, self.l = longueur, largeur
... [Link] = "rectangle"
... def str (self) :
... return "nom : {}".format([Link])
...
>>>
>>> class Carre(Rectangle) : # héritage simple
... """Sous-classe spécialisée de la super-classe Rectangle."""
... def init (self, cote=20) :
... # appel au constructeur de la super-classe de Carre :
... super(). init (cote, cote)
... [Link] = "carré" # surcharge d'attribut
...
>>>
>>> r = Rectangle()
>>> c = Carre()
>>> print(r)
nom : rectangle
>>> print(c)
nom : carré

7.7 Un exemple complet


Le script suivant 1 propose un modèle simplifié d’atome et d’ion.

La variable de classe table liste les 10 premiers éléments du tableau de M .

F 7.5 – Un Ion « est-un » Atome.

class Atome :
"""atomes simplifiés (les 10 premiers éléments)."""

table = [None, ('hydrogène',0), ('helium',2), ('lithium',4), ('beryllium',5),


('bore',6), ('carbone',6), ('azote',7), ('oxygène',8),
('fluor',10), ('neon',10)]

def init (self, nat) :


"le numéro atomique détermine le nombre de protons, d'électrons et de neutrons"
[Link], [Link] = nat, nat # nat = numéro atomique
[Link] = [Link][nat][1]

def affiche(self) :
print()
print("Nom de l'élément :", [Link][[Link]][0])
print("%s protons, %s électrons, %s neutrons" % ([Link], [Link], [Link]))

1. adapté de [1], p. 123.


7.7 Un exemple complet 55

class Ion(Atome) : # Ion hérite d'Atome


"""Les ions sont des atomes qui ont gagné ou perdu des électrons"""

def init (self, nat, charge) :


"le numéro atomique et la charge électrique déterminent l'ion"
super(). init (nat)
[Link] = [Link] - charge # surcharge
[Link] = charge

def affiche(self) : # surcharge


[Link](self)
print("Particule électrisée. Charge =", [Link])

# Programme principal =========================================================


a1 = Atome(5)
a2 = Ion(3, 1)
a3 = Ion(8, -2)
[Link]()
[Link]()
[Link]()

"""

Nom de l'élément : bore


5 protons, 5 électrons, 6 neutrons

Nom de l'élément : lithium


3 protons, 2 électrons, 4 neutrons
Particule électrisée. Charge = 1

Nom de l'élément : oxygène


8 protons, 10 électrons, 8 neutrons
Particule électrisée. Charge = -2
"""
CHAPITRE 8

La POO graphique

Très utilisée dans les systèmes d’exploitation et dans les applications, les interfaces graphiques
sont programmables en Python.

Parmi les différentes bibliothèques graphiques utilisables dans Python (GTK+,


wxPython, Qt.
. .), la bibliothèque tkinter, issue du langage tcl/Tk est installée de base dans
toutes les distributions Python. tkinter facilite la construction d’interfaces graphiques
simples.

Après avoir importé la bibliothèque, la démarche consiste à créer, configurer et


positionner les éléments graphiques (widgets) utilisés, à coder les fonctions/méthodes
associées aux wid- gets, puis d’entrer dans une boucle chargée de récupérer et traiter
les différents événements pouvant se produire au niveau de l’interface graphique :
interactions de l’utilisateur, besoins de mises à jour graphiques, etc.

8.1 Programmes pilotés par des événements


En programmation graphique objet, on remplace le déroulement séquentiel du script par une
boucle d’événe- ments (☞ Fig. 8.1)

(a) Programmation séquentielle (b) Boucle d’événements

F 8.1 – Deux styles de programmation.


58 La POO graphique

8.2 La bibliothèque tkinter


8.2.1 Présentation
C’est une bibliothèque assez simple qui provient de l’extension graphique, Tk, du langage Tcl 1.
Cette extension a largement essaimé hors de Tcl/Tk et on peut l’utiliser en Perl, Python, Ruby, etc.
Dans le cas de Python, l’extension a été renommée tkinter.
Parallèlement à Tk, des extensions ont été développées dont certaines sont utilisées en
Python. Par exemple le module standard Tix met une quarantaine de widgets à la disposition du
développeur.
De son côté, le langage Tcl/Tk a largement évolué. La version 8.5 actuelle offre une bibliothèque
ap- pelée Ttk qui permet d’« habiller » les widgets avec différents thèmes ou styles. Ce module
est également disponible à partir de Python 3.1.1.

Un exemple tkintersimple (☞ Fig. 8.2)

import tkinter

# création d'un widget affichant un simple message textuel


widget = [Link](None, text='Bonjour monde graphique !')
[Link]() # positionnement du label
[Link]() # lancement de la boucle d'événements

F 8.2 – Un exemple simple : l’affichage d’un Label

8.2.2 Les widgets de tkinter

Définition
b On appelle widget (mot valise, contraction de window et gadget) les composants graphiques
de base d’une bibliothèque.
Liste des principaux widgets de tkinter :
Tk fenêtre de plus haut niveau
Frame contenant pour organiser d’autres widgets
Label zone de message
Button bouton d’action avec texte ou
image Message zone d’affichage multi-
lignes Entry zone de saisie
Che button bouton à deux états
Radiobutton bouton à deux états exclusifs par groupe de boutons
Scale glissière à plusieurs positions
PhotoImage sert à placer des images (GIF et PPM/PGM) sur des
widgets BitmapImage sert à placer des bitmaps (X11 bitmap data) sur
des widgets Menu menu déroulant associé à un Menubutton
Menubutton bouton ouvrant un menu d’options
Scrollbar ascenseur
Listbox liste à sélection pour des textes
Text édition de texte simple ou multi-lignes
Canvas zone de dessins graphiques ou de photos
1. Langage développé en 1988 par John K. O. de l’Université de Berkeley.
8.3 Deux exemples 59

OptionMenu liste déroulante


ScrolledText widget Text avec
ascenseur PanedWindow interface à
onglets
LabelFrame contenant pour organiser d’autres widgets, avec un cadre et un titre
Spinbox un widget de sélection multiple

8.2.3 Le positionnement des widgets


tkinter possède trois gestionnaires de positionnement :
Le pa er : dimensionne et place chaque widget dans un widget conteneur selon l’espace
requis par chacun d’eux.
Le gridder : dimensionne et positionne chaque widget dans les cellules d’un tableau d’un
widget con- teneur.
Le placer : dimensionne et place chaque widget dans un widget conteneur selon l’espace
explicitement demandé. C’est un placement absolu (usage peu fréquent).

8.3 Deux exemples


8.3.1 tkPhone, un exemple sans menu
Il s’agit de créer un script de gestion d’un carnet téléphonique. L’aspect de l’application est illustré ☞ Fig.
8.3

Notion de callba
Nous avons vu que la programmation d’interface graphique passe par une boucle principale
chargée de traiter les différents évènements qui se produisent.
Cette boucle est généralement gérée directement par la bibliothèque d’interface graphique
utilisée, il faut donc pouvoir spécifier à cette bibliothèque quelles fonctions doivent être
appelées dans quels cas. Ces fonctions sont nommées des callbacks — ou rappels — car
elles sont appelées directement par la bibliothèque d’interface graphique lorsque des
évènements spécifiques se produisent.

Conception graphique
La conception graphique va nous aider à choisir les bons widgets.
En premier lieu, il est prudent de commencer par une conception manuelle ! En effet rien ne
vaut un papier, un crayon et une gomme pour se faire une idée de l’aspect que l’on veut obtenir.

Dans notre cas on peut concevoir trois zones :


1. une zone supérieure, dédiée à l’affichage ;
2. une zone médiane est une liste alphabétique ordonnée ;
3. une zone inférieure est formée de boutons de gestion de la liste ci-dessus.

Chacune de ces zones est codée par une instance de Frame(), positionnée l’une sous l’autre grâce
au
packer, et toutes trois incluses dans une instance de Tk()(cf. conception ☞ Fig. 8.3).

Le code de l’interface graphique


Méthodologie : on se propose de séparer le codage de l’interface graphique de celui des
callbacks. Pour cela on propose d’utiliser l’héritage entre une classe parente chargée de gérer
l’aspect graphique et une classe enfant chargée de gérer l’aspect fonctionnel de l’application
contenu dans les callbacks. Comme nous l’avons vu précédemment (cf. section 7.6, p. 52), c’est
un cas de polymorphisme de dérivation.

Voici donc dans un premier temps le code de l’interface graphique. L’initialisateur crée l’attribut
phoneList,une liste qu’il remplit avec le contenu du fichier contenant les données (si le fichier
n’existe pas il est créé), crée la fenêtre de base rootet appelle la méthode makeWidgets().
60 La POO graphique
Cette méthode, suit la conception graphique exposée ci-dessus et remplit chacun des trois frames.
8.3 Deux exemples 61

(a) Conception générale (b) Détails des Frame

(c) L’interface graphique.

Figure 8.3 – tkPhone.

Les callbacks sont vides (instruction pass minimale).

Comme tout bon module, un auto-test permet de vérifier le bon fonctionnement (ici le bon
aspect) de l’interface :
# -*- coding : utf-8 -*-

# Bob Cordeau
# tkPhone_IHM.py

import tkinter as tk
from [Link] import isfile

# class
class Allo_IHM :
"""IHM de l'application 'répertoire téléphonique'."""
def init (self, fic) :
"""Initialisateur/lanceur de la fenêtre de base"""
[Link] = []
[Link] = fic
if isfile([Link]) :
with open([Link]) as f :
for line in f :
[Link](line[:-1].split('*'))
else :
with open([Link], "w", encoding="utf8") :
pass

[Link]()
[Link] = [Link]()
[Link]("Allo !")
[Link](relief=[Link], bd=3)
[Link]()
[Link]()
[Link]()

def makeWidgets(self) :
"Configure et positionne les widgets"
62 La POO graphique

# frame "saisie" (en haut avec bouton d'effacement)


frameH = [Link]([Link], relief=[Link], bd=2)
[Link]()

[Link](frameH, text="Nom :").grid(row=0, column=0, sticky=tk.W)


[Link] = [Link](frameH)
[Link](row=0, column=1, sticky=tk.W, padx=5, pady=10)

[Link](frameH, text="Tel :").grid(row=1, column=0, sticky=tk.W)


[Link] = [Link](frameH)
[Link](row=1, column=1, sticky=tk.W, padx=5, pady=2)

b = [Link](frameH, text="Effacer ", command=[Link])


[Link](row=2, column=0, columnspan=2, pady=3)

# frame "liste" (au milieu)


frameM = [Link]([Link])
[Link]()

[Link] = [Link](frameM)
[Link] = [Link](frameM, yscrollcommand=[Link], height=6)
[Link](command=[Link])
[Link](side=[Link], fill=tk.Y, pady=5)
[Link](side=[Link], fill=[Link], expand=1, pady=5)
## remplissage de la Listbox
for i in [Link] :
[Link]([Link], i[0])
[Link]("<Double-Button-1>", lambda event : [Link](event))

# frame "boutons" (en bas)


frameB = [Link]([Link], relief=[Link], bd=3)
[Link](pady=3)

b1 = [Link](frameB, text="Ajouter ", command=[Link])


b2 = [Link](frameB, text="Supprimer", command=[Link])
b3 = [Link](frameB, text="Afficher ", command=[Link])
[Link](side=[Link], pady=2)
[Link](side=[Link], pady=2)
[Link](side=[Link], pady=2)

def ajouter(self) :
pass

def supprimer(self) :
pass

def afficher(self, event=None) :


pass

def clear(self) :
pass

# auto-test
if name == ' main ' :
# instancie l'IHM, callbacks inactifs
app = Allo_IHM("./[Link]")

Le code de l’application
On va maintenant utiliser le module de la façon suivante :
– On importe la classe Allo_IHM depuis le module précédent ;
– on crée une classe Allo qui en dérive ;
– son initialisateur appelle l’initialisateur de la classe de base pour hériter de toutes ses
caractéris- tiques ;
– il reste à surcharger les
callbacks. Enfin, le script instancie

# -*- coding : utf-8 -*-

l’application.
8.3 Deux exemples 63

# Bob Cordeau
# [Link]

# import
from tkPhone_IHM import Allo_IHM

# définition de classe
class Allo(Allo_IHM) :
"""Repertoire téléphonique."""

def init (self, fic='[Link]') :


"Constructeur de l'IHM."
super(). init (fic)

def ajouter(self) :
# maj de la liste
ajout = ["", ""]
ajout[0] = [Link]()
ajout[1] = [Link]()
if (ajout[0] == "") or (ajout[1] == "") :
return
[Link](ajout)
[Link]()
# maj de la listebox
[Link](0, 'end')
for i in [Link] :
[Link]('end', i[0])
[Link]()
[Link]()
# maj du fichier
f = open([Link], "a")
[Link]("%s*%s\n" % (ajout[0], ajout[1]))
[Link]()

def supprimer(self) :
[Link]()
# maj de la liste
retrait = ["", ""]
retrait[0], retrait[1] = [Link][int([Link]()[0])]
[Link](retrait)
# maj de la listebox
[Link](0, 'end')
for i in [Link] :
[Link]('end', i[0])
# maj du fichier
f = open([Link], "w")
for i in [Link] :
[Link]("%s*%s\n" % (i[0], i[1]))
[Link]()

def afficher(self, event=None) :


[Link]()
name, phone = [Link][int([Link]()[0])]
[Link](0, name)
[Link](0, phone)

def clear(self) :
[Link](0, 'end')
[Link](0, 'end')

# programme principal =========================================================


app = Allo() # instancie l'application
64 La POO graphique

8.3.2 IDLE, un exemple avec menu


Généralement les distributions Python comporte l’application IDLE, l’interpréteur/éditeur écrit en
Python par Guido van R 1. Cette application se présente sous l’aspect d’une interface

graphique complète (☞ Fig. 8.4), avec menu.


C’est un source Python dont le code est disponible 2 et constitue à lui seul un cours complet à tkinter.

(a) L’interpréteur d’IDLE

(b) L’éditeur d’IDLE

FIgure 8.4 – IDLE.

1. Dans certaines distributions GNU/Linux, IDLE est un package particulier.


2. Mais il est trop volumineux pour être reproduit dans ces notes. . .
8.3 Deux exemples 65
CHAPITRE 9

Quelques techniques avancées de


programmation

Ce chapitre présente quelques exemples de techniques avancées dans les trois


paradigmes que supporte Python, les programmations procédurale, objet et
fonctionnelle.

9.1 Techniques procédurales


9.1.1 Le pouvoir de l’introspection
C’est un des atouts de Python. On entend par introspection la possibilité d’obtenir des
informations sur les objets manipulés par le langage.

La fonction help()

On peut tout d’abord utiliser la fonction prédéfinie help().


Cette fonction est auto-documentée :
>>> help()

Welcome to Python 3.2! This is the online help utility.

If this is your first time using Python, you should definitely check out
the tutorial on the Internet at http ://[Link]/tutorial/.

Enter the name of any module, keyword, or topic to get help on writing
Python programs and using Python modules. To quit this help utility and
return to the interpreter, just type "quit".

To get a list of available modules, keywords, or topics, type "modules",


"keywords", or "topics". Each module also comes with a one-line summary

such as "spam", type "modules spam".

help> []
no Python documentation found for '[]'

help> quit

You are now leaving help and returning to the Python interpreter.
If you want to ask for help on a particular object directly from the
interpreter, you can type "help(object)". Executing "help('string')"
has the same effect as typing a particular string at the help> prompt.
>>>
66 Techniques avancées

Si on l’appelle par exemple sur l’objet liste, on obtient (vue partielle) :


>>> help([])
Help on list object :

class list(object)
| list() -> new empty list
| list(iterable) -> new list initialized from iterable`s items

| Methods defined here :

| add (...)
| x. add (y) <==> x+y

| (...)
| x. (y) <==> y in x

...

| reverse(...)
| [Link]() -- reverse *IN PLACE*

| sort(...)
| [Link](cmp=None, key=None, reverse=False) -- stable sort *IN PLACE* ;
| cmp(x, y) -> -1, 0, 1

|
| Data and other attributes defined here :

| hash = None

| new = <built-in method new of type object>


| T. new (S, ...) -> a new object with type S, a subtype of T

La fonction utilitaire printInfo()filtre les méthodes disponibles de son argument ne commençant


pas par « _» et affiche les docstrings associées sous une forme plus lisible que help():
def printInfo(object) :
"""Filtre les méthodes disponibles de <object>."""
methods = [method for method in dir(object)
if callable(getattr(object, method)) and not [Link]('_')]

for method in methods :


print(getattr(object, method). doc )

Par exemple, l’appel :


printInfo([])

affiche la documentation :
[Link](object) -- append object to end
[Link](value) -> integer -- return number of occurrences of value
[Link](iterable) -- extend list by appending elements from the iterable
[Link](value, [start, [stop]]) -> integer -- return first index of value.
Raises ValueError if the value is not present.
[Link](index, object) -- insert object before index
[Link]([index]) -> item -- remove and return item at index (default last).
Raises IndexError if list is empty or index is out of range.
[Link](value) -- remove first occurrence of value.
Raises ValueError if the value is not present.
[Link]() -- reverse *IN PLACE*
[Link](cmp=None, key=None, reverse=False) -- stable sort *IN PLACE* ;
cmp(x, y) -> -1, 0, 1

Les fonctions type(), dir() et id()

Elle fournissent respectivement le type, les méthodes et la localisation mémoire (unique) d’un
objet :
>>> li = [1, 2, 3]
>>>
>>> type(li)
9.1 Techniques procédurales 67

<class 'list'>
>>>
>>> dir(li)
[' add ', ' class ', ' contains ', ' delattr ', ' delitem ', ' doc ', ' eq ', ' format ', ' ge ', '
getattribute ', ' getitem ', ' gt ', ' hash ', ' iadd ', ' imul ', ' init ', ' iter ', ' le
', ' len ', ' lt ', ' mul ', ' ne ', ' new ', ' reduce ', ' reduce_ex ', ' repr ', ' reversed
', ' rmul ', ' setattr ', ' setitem ', ' sizeof ', ' str ', ' subclasshook ', 'append', 'count', '
extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
>>>
>>> id(li)
3074801164

Les fonctions locals() et globals()

Comme nos l’avons vu au chapitre 5.3.1, p. 37, ces fonctions retournent respectivement le
dictionnaire des noms locaux (globaux) au moment de leur appel.

Le module sys

Ce module fournit nombre d’informations générales, entre autres :


>>> import sys
>>>
>>> [Link]
'/usr/bin/python3'
>>>
>>> [Link]
'linux2'
>>>
>>> [Link]
'3.2.3 (default, Oct 19 2012, 20:13:42) \n[GCC 4.6.3]'
>>>
>>> [Link]
['']
>>>
>>> [Link]
['', '/usr/lib/python3.2', '/usr/lib/python3.2/plat-linux2', '/usr/lib/python3.2/lib-dynload', '/usr/local/lib/python3
.2/dist-packages', '/usr/lib/python3/dist-packages']
>>>
>>> [Link]
{'reprlib' : <module 'reprlib' from '/usr/lib/python3.2/[Link]'>, 'heapq' : <module 'heapq' from '/usr/lib/python3
.2/[Link]'>,
'sre_compile' : <module 'sre_compile' from '/usr/lib/python3.2/sre_compile.py'>,
...

L’interpréteur IPython
Cet interpréteur qui vient récemment de passer en version 1.0 est certainement le meilleurs outil
disponible, spécialement dans les domaines de la documentation et de l’introspection.

F 9.1 – L’interpréteur IPython.

Ipython est également très utile pour la recherche des erreurs de programmation (bugs).
68 Techniques avancées

9.1.2 Gestionnaire de contexte (ou bloc gardé) :


Cette syntaxe simplifie le code en assurant que certaines opérations sont exécutées avant et
après un bloc d’instructions donné. Illustrons ce mécanisme sur un exemple classique ou il
importe de fermer le fichier utilisé :

# au lieu de ce code :
try :
fh = open(filename)
for line in fh :
process(line)
finally :
[Link]()

# il est plus simple d'écrire :


with open(filename) as fh :
for line in fh :
process(line)

9.1.3 Utiliser un dictionnaire pour lancer des fonctions ou des méthodes


L’idée est d’exécuter différentes parties de code en fonction de la valeur d’une variable de
contrôle. On peur se servir de cette technique pour implémenter un menu textuel.

animaux = []
nombre_de_felins = 0

def gererChat() :
global nombre_de_felins
print("Miaou")
[Link]("félin")
nombre_de_felins += 1

def gererChien() :
print("Ouah")
[Link]("canidé")

def gererOurs() :
print("Attention au *OUILLE* !")
[Link]("plantigrade")

# =============================================================================

dico = {
"chat" : gererChat,
"chien" : gererChien,
"ours" : gererOurs

betes = ["chat", "ours", "chat", "chien"] # une liste d'animaux rencontrés

for bete in betes :


dico[bete]() # appel de la fonction correspondante

nf = nombre_de_felins
print("nous avons rencontré {} félin(s)".format(nf))
print("Les animaux rencontrés sont : {}".format(', '.join(animaux), end=" ")

"""
Miaou
Attention au *OUILLE* !
Miaou
Ouah
nous avons rencontré 2 félin(s)
Les animaux rencontrés sont : félin, plantigrade, félin, canidé
"""
9.1 Techniques procédurales 69

9.1.4 Les fonctions récursives


Définition
Une fonction récursive peut s’appeler elle-même.
Par exemple, trier un tableau de N éléments par ordre croissant c’est extraire le plus petit
élément puis trier le tableau restant à N − 1 éléments.
Les fonction récursives sont souvent utilisées pour traiter les structures arborescentes
comme les répertoires dans les systèmes de fichiers des disques durs. Voici l’exemple d’une
fonction qui affiche récur- sivement les fichiers d’un répertoire fourni en paramètre :

#-*- coding : utf8 -*-

from os import listdir


from [Link] import isdir, join

def listeFichiersPython(repertoire) :
"""Affiche récursivement les fichiers Python à partir de <repertoire>."""
noms = listdir(repertoire)
for nom in noms :
if nom in (".", "..") :
continue
nom_complet = join(repertoire, nom)
if isdir(nom_complet) :
listeFichiersPython(nom_complet)
elif [Link](".py") or [Link](".pyw") :
print("Fichier Python :", nom_complet)

listeFichiersPython("/home/bob/Tmp")

Dans cette définition, on commence par constituer dans la variable noms la liste des fichiers et
réper- toires du répertoire donné en paramètre. Puis, dans une boucle for, tant que l’élément
examiné est un répertoire, on ré-appelle la fonction sur lui pour descendre dans l’arborescence
de fichiers tant que la condition terminale (if nom in (".", ".."):) est fausse.
Le résultat produit est :
Fichier Python : /home/bob/Tmp/parfait_chanceux.py
Fichier Python : /home/bob/Tmp/[Link]
Fichier Python : /home/bob/Tmp/parfait_chanceux_m.py
Fichier Python : /home/bob/Tmp/verif_m.py
Fichier Python : /home/bob/Tmp/Truc/Machin/tkPhone_IHM.py
Fichier Python : /home/bob/Tmp/Truc/Machin/[Link]
Fichier Python : /home/bob/Tmp/Truc/[Link]
Fichier Python : /home/bob/Tmp/Truc/tk_variable.py

9.1.5 Les listes définies en compréhension


Les listes définies « en compréhension », souvent appelées « compréhension de listes »,
permettent de générer ou de modifier des collections de données par une écriture lisible, simple
et performante.

Cette construction syntaxique se rapproche de la notation utilisée en mathématiques :

{x2 |x ∈ [2, 11[} ⇔ [x**2 for x in range(2, 11)]⇒ [4, 9, 16, 25, 36, 49, 64, 81, 100]

Définition
Une liste en compréhension est équivalente à une boucle forqui construirait la même liste en
utilisant
la méthode append().

Les listes en compréhension sont utilisables sous trois formes.


Première forme expression d’une liste simple de valeurs :
result1 = [x+1 for x in une_seq]
# a le même effet que :
result2 = []
for x in une_seq :
[Link](x+1)
70 Techniques avancées

Deuxième forme expression d’une liste de valeurs avec filtrage :


result3 = [x+1 for x in une_seq if x > 23]
# a le même effet que :
result4 = []
for x in une_seq :
if x > 23:
[Link](x+1)

Troisième forme expression d’une combinaison de listes de valeurs :


result5 = [x+y for x in une_seq for y in une_autre]
# a le même effet que :
result6 = []
for x in une_seq :
for y in une_autre :
[Link](x+y)

Des utilisations très pythoniques :

valeurs_s = ["12", "78", "671"]


# conversion d'une liste de chaînes en liste d'entier
valeurs_i = [int(i) for i in valeurs_s] # [12, 78, 671]

# calcul de la somme de la liste avec la fonction intégrée sum


print(sum([int(i) for i in valeurs_s])) # 761

# a le même effet que :


s = 0
for i in valeurs_s :
s = s + int(i)
print(s) # 761

# Initialisation d'une liste 2D


multi_liste = [[0]*2 for ligne in range(3)]
print(multi_liste) # [[0, 0], [0, 0], [0, 0]]

Autre exemple :
>>> C_deg = range(-20, 41, 5)
>>> F_deg = [(9.0/5)*c + 32 for c in C_deg]
>>> table = [C_deg, F_deg]
>>> for i in range(len(table[0])) :
... print(table[0][i], "=>", table[1][i])
...
-20 => -4.0
-15 => 5.0
-10 => 14.0
-5 => 23.0
0 => 32.0
5 => 41.0
10 => 50.0
15 => 59.0
20 => 68.0
25 => 77.0
30 => 86.0
35 => 95.0
40 => 104.0

9.1.6 Les dictionnaires définis en compréhension


Comme pour les listes, on peut définir des dictionnaires en compréhension :
>>> {n : x**2 for n, x in enumerate(range(5))}
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

On note d’utilisation des accolades et du double point caractéristiques de la syntaxe du


dictionnaire.

9.1.7 Les ensembles définis en compréhension


De même, on peut définir des ensembles en compréhension :
9.1 Techniques procédurales 71

>>> {n for n in range(5)}


set([0, 1, 2, 3, 4])

Dans ce cas les accolades sont caractéristiques de la syntaxe de l’ensemble.

9.1.8 Les générateurs et les expressions génératrices


Les générateurs
Définition
Un générateur est une fonction qui mémorise son état au moment de retourner une valeur.
La transmission d’une valeur s’effectue en utilisant le mot clé yield.

Les générateurs fournissent un moyen de générer des exécutions paresseuses, ce qui signifie
qu’elles ne calculent que les valeurs réellement demandées. Ceci peut s’avérer beaucoup plus
efficace (en termes de mémoire) que le calcul, par exemple, d’une énorme liste en une seule fois.

Techniquement, un générateur fonctionne en deux temps. D’abord, au lieu de retourner


une valeur, la fonction fournit un objet générateur en utilisant le mot clé yield. Le corps de la fonction
n’est exécuté qu’ensuite, lors des appels explicites à la méthode next() ou implicites dans une
boucle for.

Voici un exemple de générateur qui fournit un compteur d’entiers (initialisé à 0) inférieurs ou


égaux à l’argument du générateur :
def counter(maximum) :
"""génère des entiers inférieurs ou égaux à <maximum>."""
i = 0
while True :
yield i
if i == maximum : # arrêt de générateur
return
i = i + 1

for val in counter(5) :


print(val, end=" ")

Ce qui produit :
0 1 2 3 4 5

Les expressions génératrices


Syntaxe
Une expression génératrice possède une syntaxe presque identique à celle des listes en
compréhen sion ; la différence est qu’une expression génératrice est entourée de parenthèses.

Utilisation
Les expressions génératrices (souvent appelée « genexp ») sont aux générateurs ce que les
listes en com- préhension sont aux fonctions. Bien qu’il soit transparent, le mécanisme du yield vu
ci-dessus est encore en action.

Par exemple l’expression suivante génère la création d’un million de valeurs en mémoire avant
de commencer la boucle :
for i in [x**2 for x in range(1000000)]:

Alors que dans l’expression suivante, la boucle commence immédiatement et génère le


million de valeurs au fur et à mesure des demandes :
for i in (x**2 for x in range(1000000)) :

Voici un autre exemple : une genexp de recherche d’un motif dans un fichier, analogue à un
grep
simplifié 1 :
1. Célèbre utilitaire Unix.
72 Techniques avancées

>>> with open('.bashrc') as f :


... file = [Link]()
...
>>> lines = ([Link]() for line in file if [Link]("alias"))
>>> for line in lines :
... print(line)
...
alias la='ls -A'
alias l='ls -CF'
alias c='clear'
alias i='ipython3 qtconsole &'
alias alert='notify-send --urgency=low -i "$([ $ ? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e
'\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'

9.1.9 Les fonctions incluses


La syntaxe de définition des fonctions en Python permet tout à fait d’emboîter leur définition.
Distinguons deux cas d’emploi :
• Idiome de la fonction fabrique renvoyant une fermeture :
>>> def creer_plus(ajout) :
... """Fonction 'fabrique'."""
... def plus(increment) :
... """Fonction 'fermeture' : utilise des noms locaux à creer_plus()."""
... return increment + ajout
... return plus
...
>>>
>>> p = creer_plus(23)
>>> q = creer_plus(42)
>>>
>>> print("p(100) =", p(100))
('p(100) =', 123)
>>> print("q(100) =", q(100))
('q(100) =', 142)

• Fonction fabrique renvoyant une classe :


>>> class CasNormal :
... def uneMethode(self) :
... print("normal")
...
>>>
>>> class CasSpecial :
... def uneMethode(self) :
... print("spécial")
...
>>>
>>> def casQuiConvient(estNormal=True) :
... """Fonction fabrique renvoyant une classe."""
... if estNormal :
... return CasNormal()
... else :
... return CasSpecial()
...
>>>
>>> une_instance = casQuiConvient()
>>> une_instance.uneMethode()
normal
>>> une_instance = casQuiConvient(False)
>>> une_instance.uneMethode()
spécial

9.1.10 Les décorateurs


Les décorateurs Python sont des fonctions qui permettent d’effectuer des prétraitements lors
de l’appel d’une fonction, d’une méthode ou d’une classe.

Syntaxe
Soit deco() un décorateur. Pour « décorer » une fonction on écrit :
9.2 Techniques objets 73

def deco() :
...

@deco
def fonction(arg1, arg2, ...) :
pass

Une fonction peut être multi-décorée :


def f1() :
...

def f2() :
...

def f3() :
...

@f1 @f2 @f3


def g() :
pass

Voici un exemple simple :


def unDecorateur(f) :
cptr = 0
def _interne(*args, **kwargs) :
nonlocal cptr
cptr = cptr + 1
print("Fonction decorée :", f. name , ". Appel numéro :", cptr)
return f(*args, **kwargs)

return _interne

@unDecorateur
def uneFonction(a, b) :
return a + b

def autreFonction(a, b) :
return a + b

# programme principal ===============================================


## utilisation d'un décorateur
print(uneFonction(1, 2))
## utilisation de la composition de fonction
autreFonction = unDecorateur(autreFonction)
print(autreFonction(1, 2))

print(uneFonction(3, 4))
print(autreFonction(6, 7))
"""
Fonction decorée : uneFonction. Appel numéro 1
3
Fonction decorée : autreFonction. Appel numéro 1
3
Fonction decorée : uneFonction. Appel numéro 2
7
Fonction decorée : autreFonction. Appel numéro 2
13
"""

9.2 Techniques objets


Comme nous l’avons vu lors du chapitre précédent, Python est un langage complètement
objet. Tous les types de base ou dérivés sont en réalité des types abstraits de données
implémentés sous forme de classe.
74 Techniques avancées

9.2.1 Les Functors


En Python un objet fonction ou functor est une référence à tout objet « appelable » 1 : fonction,
fonction anonyme lambda 2, méthode, classe. La fonction prédéfinie callable() permet de tester cette
propriété :
>>> def maFonction() :
... print('Ceci est "appelable"')
...
>>> callable(maFonction)
True
>>> chaine = 'Ceci est "appelable"'
>>> callable(chaine)
False

Il est possible de transformer les instances d’une classe en functor si la méthode spéciale
call ()est définie dans la la classe :
>>> class A :
... def init (self) :
... [Link] = []
... def call (self, a, b) :
... [Link]((a, b))
... return a + b
...
>>> a = A()
>>> a(1, 2)

>>> a(3, 4)

>>> a(5, 6)
11
>>> [Link]
[(1, 2), (3, 4), (5, 6)]

9.2.2 Les accesseurs


Le problème de l’encapsulation
Dans le paradigme objet, l’état d’un objet est privé, les autres objets n’ont pas le droit de le
consulter ou de le modifier.
Classiquement, on distingue les visibilités suivantes :
– publique ;
– protégée ;
– privée.

F 9.2 – En Python, tous les attributs (données, méthodes) sont publics !

On peut néanmoins modifier cet état de fait.


Une simple convention courante est d’utiliser des noms commençant par un souligné ( _) pour
signifier attribut protégé. Par exemple _attrib. Cette solution est avant tout destinée à éviter les
collisions de noms dans le cas où l’attribut serait redéfini dans une classe dérivée.
Mais Python n’oblige à rien 3, c’est au développeur de respecter la convention !

Python propose un mécanisme 4 pour émuler les attribut privés : les identificateurs de la classe
com- mencent par deux soulignés. Par exemple ident. Mais cette protection reste déclarative
et n’offre pas une sécurité absolue.
1. callable en anglais.
2. Cette notion sera développée section 9.3.1, p. 78.
3. Slogan des développeurs Python : « We’re all consenting adults here » (nous sommes entre adultes consentants).
4. le name mangling.
9.2 Techniques objets 75

La solution property

Le principe de l’encapsulation est mis en œuvre par la notion de propriété.


Définition
Une propriété (property) est un attribut d’instance possédant des fonctionnalités spéciales.

Deux syntaxes implémentent cette solution.

La première définit explicitement la propriété x et ses quatre paramètres (dans l’ordre :


méthode de lecture, méthode de modification, méthode de suppression, chaîne de
# !/usr/bin/python3
#-*- coding : utf-8 -*-
# fichier : [Link]

class C :
def init (self) :
self._ma_propriete = None

def getx(self) :
"""getter."""
return self._x

def setx(self, value) :


"""setter."""
self._x = value

def delx(self) :
"""deleter."""
del self._x

x = property(getx, setx, delx, "Je suis la propriété 'x'.")

# auto-test =========================================================
if name == ' main ' :
test = C()

test.x = 10 # setter

print(test.x) # getter

print(C.x. doc ) # documentation

"""
10
Je suis la propriété 'x'.
"""

documentation) :

La seconde, plus simple, utilise la syntaxe des décorateurs. On remarque que la chaîne de
documenta- tion de la property est ici la docstring de la définition de la propriété x:
# !/usr/bin/python3
#-*- coding : utf-8 -*-
# fichier : [Link]

class C :
def init (self) :
self._x = None

@property
def x(self) :
"""Je suis la propriété 'x'."""
return self._x

@[Link]
def x(self, value) :
self._x = value

@[Link]
def x(self) :
76 Techniques avancées

del self._x

# auto-test =========================================================
if name == ' main ' :
test = C()

test.x = 10 # setter

print(test.x) # getter

print(C.x. doc ) # documentation

"""
10
Je suis la propriété 'x'.
"""

Un autre exemple : la classe Cercle

Schéma de conception : nous allons tout d’abord définir une classe Point que nous utiliserons
comme classe de base de la classe Cercle.

F 9.3 – Conception UML de la classe Cercle.

Voici le code de la classe Point :


class Point :

def init (self, x=0, y=0) :


self.x, self.y = x, y

@property
def distance_origine(self) :
return [Link](self.x, self.y)

def eq (self, other) :


return self.x == other.x and self.y == other.y

def str (self) :


return "({0.x !s}, {0.y !s})".format(self)

L’utilisation de propertypermet un accès en lecture seule au résultat de la méthode distance_origine()


considérée alors comme un simple attribut (car on l’utilise sans parenthèse) :
p1, p2 = Point(), Point(3, 4)
print(p1 == p2) # False
print(p2, p2.distance_origine) # (3, 4) 5.0

De nouveau, les méthodes renvoyant un simple flottant seront utilisées comme des attributs
grâce à
property :
9.2 Techniques objets 77

class Cercle(Point) :
def init (self, rayon, x=0, y=0) :
super(). init (x, y)
[Link] = rayon

@property
def aire(self) : return [Link] * ([Link] ** 2)

@property
def circonference(self) : return 2 * [Link] * [Link]

@property
def distance_bord_origine(self) :
return abs(self.distance_origine - [Link])

Voici la syntaxe permettant d’utiliser la méthode rayon comme un attribut en lecture-écriture.


Remar- quez que la méthode rayon()retourne l’attribut protégé : rayonqui sera modifié par le se er (la
méthode modificatrice) :
@property
def rayon(self) :
return self. rayon

@[Link]
def rayon(self, rayon) :
assert rayon > 0, "rayon strictement positif"
self. rayon = rayon

Exemple d’utilisation des instances de Cercle:


def eq (self, other) :
return ([Link] == [Link]
and super(). eq (other))

def str (self) :


return ("{0. class . name }({[Link] !s}, {0.x !s}, "
"{0.y !s})".format(self))

if name == " main " :


c1 = Cercle(2, 3, 4)
print(c1, [Link], [Link])
# Cercle(2, 3, 4) 12.5663706144 12.5663706144
print(c1.distance_bord_origine, [Link]) # 3.0 2
[Link] = 1 # modification du rayon
print(c1.distance_bord_origine, [Link]) # 4.0 1

9.2.3 Le duck typing


Il existe un style de programmation très pythonique appelé : duck typing :
« S’il marche comme un canard et cancane comme un canard, alors c’est un canard ! ».
Cela signifie que Python ne s’intéresse qu’au comportement des objets. Par exemple un objet
fichier peut être créé par open() ou par une instance de [Link]. Les deux approches offrent la
même API (interface de programmation), c’est-à-dire les mêmes méthodes et l’utilisateur l’utilise
de la même façon.

Autre exemple :
class Duck :
def quack(self) :
print("Quaaaaaack !")

def feathers(self) :
print("The duck has white and gray feathers.")

class Person :
def quack(self) :
print("The person imitates a duck.")

def feathers(self) :
print("The person takes a feather from the ground and shows it.")
78 Techniques avancées

def in_the_forest(duck) :
[Link]()
[Link]()

def game() :
donald = Duck()
john = Person()
in_the_forest(donald)
in_the_forest(john)

if name == ' main ' :


game()

"""
Quaaaaaack !
The duck has white and gray feathers.
The person imitates a duck.
The person takes a feather from the ground and shows it.
"""

9.3 Techniques fonctionnelles


9.3.1 Directive lambda

Issue de langages fonctionnels (comme Lisp), la directive lambdapermet de définir un objet fonction
anonyme dont le bloc d’instructions est limité à une expression dont l’évaluation fournit la valeur
de retour de la fonction.
Syntaxe
lambda [parameters] :expression

Par exemple cette fonction retourne « s » si son argument est différent de 1, une chaîne vide
>>> s = lambda x : "" if x == 1 else "s"
>>>
>>> s(3)
's'
>>> s(1)
''

sinon :

Autres exemples illustrant les différences de syntaxe fonction/lambda :


>>> def f(x) :
... return x**2
...
>>> print(f(8))
64
>>>
>>> g = lambda x : x**2
>>> print(g(8))
64

9.3.2 Les fonctions map, filter et reduce

La programmation fonctionnelle est un paradigme de programmation qui considère le calcul en


tant qu’é- valuation de fonctions mathématiques. Elle souligne l’application des fonctions,
contrairement au modèle de programmation impérative qui met en avant les changements d’état
1. Elle repose sur trois concepts : mapping, filtering et reducing qui sont implémentés en Python

par trois fonctions : map(), filter() et


reduce().

La fonction map() :

map() applique une fonction à chaque élément d’une séquence et retourne un itérateur :

1. cf. Wikipedia
9.3 Techniques fonctionnelles 79

>>> map(lambda x :x, range(10))


<map object at 0x7f3a80104f50>
>>>
>>> list(map(lambda x :x, range(10)))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

On remarque que map() peut être remplacée par un générateur en compréhension.

La fonction filter() :

filter() construit et renvoie un itérateur sur une liste qui contient tous les éléments de la
séquence initiale répondant au critère :
function(element) == True :
>>> list(filter(lambda x : x > 4, range(10)))
[5, 6, 7, 8, 9]

De même filter() peut être remplacée par un générateur en compréhension.

La fonction reduce() :

reduce() est une fonction du module functools. Elle applique de façon cumulative une fonction de
deux arguments aux éléments d’une séquence, de gauche à droite, de façon à réduire cette
séquence à une seule valeur qu’elle renvoie :
>>> def somme(x, y) :
... print x, '+', y
... return x + y
...
>>> reduce(somme, [1, 2, 3, 4])
1 + 2
3 + 3
6 + 4
10
>>>
>>> sum([1, 2, 3, 4])
10

On remarque que reduce() peut être remplacée par une des fonctions suivantes : all(), any(), max(),
min()ou sum().

9.3.3 Les applications partielles de fonctions


Issue de la programmation fonctionnelle, une PFA (application partielle de fonction) de n
paramètres prend le premier argument comme paramètre fixe et retourne un objet fonction (ou
instance) utilisant les n-1 arguments restants.
Les PFA sont très utiles dans les fonctions de calcul comportant de nombreux paramètres.
On peut en fixer certains et ne faire varier que ceux sur lesquels on veut agir.

>>> from functools import partial


>>>
>>> def f(m, c, d, u) :
... return 1000*m + 100*c + 10*d + u
...
>>>
>>> f(1, 2, 3, 4)
1234
>>>
>>> g = partial(f, 1, 2, 3)
>>> g(4)
1234
>>> h = partial(f, 1, 2)
>>> h(3, 4)
1234

Elles sont aussi utiles pour fournir des modèles partiels de widgets, qui ont souvent de
nombreux paramètres. Dans l’exemple suivant, on redéfinit la classe Button en fixant certains de
ses attributs (qui peuvent toujours être surchargés) :
80 Techniques avancées

Figure 9.4 – PFA appliquée à un widget

from functools import partial


import tkinter as tk

root = [Link]()
# instanciation partielle de classe :
MonBouton = partial([Link], root, fg='purple', bg='green')
MonBouton(text="Bouton 1").pack()
MonBouton(text="Bouton 2").pack()
MonBouton(text="QUITTER", bg='red', fg='black',
command=[Link]).pack(fill=tk.X, expand=True)
[Link]("PFA !")
[Link]()

Ce résultat est illustré par la figure 9.4.

9.4 La persistance et la sérialisation


Définition
La persistence consiste à sauvegarder des données afin qu’elles survivent à l’arrêt de
l’application.
On peut distinguer deux étapes :
• la sérialisation et la désérialisation ;
• le stockage et l’accès.
La sérialisation est le processus de conversion d’un ensemble d’objets en un flux
d’octets. La désérialisation est le processus inverse qui recrée les données
d’origine.
Le sto age utilise soit des fichiers, soit des bases de données.

Examinons des exemples simples.

9.4.1 Sérialisation avec pickle et json

Le module pickle

L’intérêt du module pickleest sa simplicité. Par contre, ce n’est pas un format répendu : il n’est utile
que tant que l’on reste dans le monde Python.

On peut utiliser une chaîne pour sérialiser, mais l’usage le plus commun est d’utiliser un
fichier. Par exemple pour un dictionnaire :
import pickle

favorite_color = {"lion" : "jaune", "fourmi" : "noire", "caméléon" : "variable"}


# stocke ses données dans un fichier
[Link](favorite_color, open("save.p", "wb"))

# retrouver ses données : pickle recrée un dictionnaire


dico = [Link](open("save.p", "rb"))
print(dico)

La lecture du fichier [Link] :


{'fourmi' : 'noire', 'lion' : 'jaune', 'caméléon' : 'variable'}
9.4 La persistance et la sérialisation 81

Le module json

Le module jsonpermet d’encoder et de décoder des informations au format JSON1. C’est un format
d’échange très utile, implémenté dans un grand nombre de langages, plus lisible que XMLmais
moins puissant.
On utilise la même syntaxe qu’avec pickle, à savoir dumps() et loads() pour une chaîne, dump() et
load() pour un fichier :

import json

# encodage dans un fichier


with open("json_tst", "w") as f :
[Link](['foo', {'bar' :('baz', None, 1.0, 2)}], f)

# décodage
with open("json_tst") as f :
print([Link](f))

La lecture du fichier json_tst produit :


['foo', {'bar' : ['baz', None, 1.0, 2]}]

9.4.2 Stockage avec sqlite3


Le module sqlite3 est une bibliothèque écrite en C qui implémente une base de données
relationnelle légère qui utilise des fichiers (ou même la mémoire).
Cette utilisation convient bien à de petits volumes de données et permet de maquetter le
passage à des bases de données plus importantes.

Exemple de stockage d’une table :


import sqlite3

conn = [Link]('/home/bob/Tmp/tel_bd') # création du connecteur


with conn as c : # création du curseur
# création de la table
[Link]("""create table tel (nom text, prenom text, numero integer)""")
# insertion d'une ligne de données
[Link]("""insert into tel values ('Barthod', 'Stéphane', '86451')""")
[Link]("""insert into tel values ('Bourrely', 'Jean', '86617')""")
[Link]("""insert into tel values ('Brunel', 'Michaël', '86619')""")
[Link]("""insert into tel values ('Canal', 'Bruno', '86604')""")
[Link]("""insert into tel values ('Cordeau', 'Bob', '86624')""")
[Link]("""insert into tel values ('Donath', 'Therèse', '86600')""")
[Link]("""insert into tel values ('Marie', 'Florence', '86602')""")

Le fichier tel_bd produit peut être visualisé par le programme SQLite database browser :

F 9.5 – Visualisation d’un fichier de base de données sqlite3

1. JavaScript Object Notation.


82 Techniques avancées

9.5 Les tests


Dès lors qu’un programme dépasse le stade du petit script, le problème des erreurs et donc des
tests se pose inévitablement 1.
Définition
Un test consiste à appeler la fonctionnalité spécifiée dans la documentation, avec un scénario
qui correspond à un cas d’utilisation, et à vérifier que cette fonctionnalité se comporte comme
prévu.

9.5.1 Tests unitaires et tests fonctionnels


On distingue deux familles de test :
Tests unitaires : validations isolées du fonctionnement d’une classe, d’une méthode ou d’une
fonction. Par convention, chaque module est associé à un module de tests unitaires,
placé dans un réper- toire tests du paquet. Par exemple, un module nommé [Link] aura
un module de tests nommé
tests/test_calculs.py
Tests fonctionnels : prennent l’application complète comme une « boîte noire » et la manipulent
comme le ferait l’utilisateur final. Ces tests doivent passer par les mêmes interfaces que
celles fournies aux utilisateurs, c’est pourquoi ils sont spécifiques à la nature de
l’application et plus délicats à mettre en œuvre.

Dans cette introduction, nous nous limiterons à une courte présentation des tests
unitaires.

9.5.2 Module unittest

Le module Python unittest fournit l’outil PyUnit, outil que l’on retrouve dans d’autres langages : JUnit
(Java), NUnit (.Net), JSUnit (Javascript), tous dérivés d’un outil initialement développé pour le
langage SmallTalk : SUnit.
PyUnit propose une classe de base, TestCase. Chaque méthode implémentée dans une
classe dérivée de TestCase, et préfixée de test_, sera considérée comme un test unitaire 2 :

"""Module de calculs."""

# fonctions
def moyenne(*args) :
"""Renvoie la moyenne."""
length = len (args)
sum = 0
for arg in args :
sum += arg
return sum/length

def division(a, b) :
"""Renvoie la division."""
return a/b

"""Module de test du module de calculs."""

# import
import sys
import unittest
from [Link] import abspath, dirname

[Link](0, dirname(dirname((abspath( file )))))


from calculs import moyenne, division

# définition de classe et de fonction


class CalculTest([Link]) :

def test_moyenne(self) :
[Link](moyenne(1, 2, 3), 2)

1. Cf. la loi de M .
2. cf. [7], p. 123.
9.6 La documentation des sources 83

[Link](moyenne(2, 4, 6), 4)

def test_division(self) :
[Link](division(10, 5), 2)
[Link](ZeroDivisionError, division, 10, 0)

def test_suite() :
tests = [[Link](CalculTest)]
return [Link](tests)

# auto-test ===================================================================
if name == ' main ' :
[Link]()

"""
..

Ran 2 tests in 0.000s

OK
"""

Pour effectuer une « campagne de tests », il reste à créer un script qui :


• recherche tous les modules de test : leurs noms commencent par test_ et ils sont contenus
dans un répertoire tests;
• récupère la suite, renvoyée par la fonction globale test_suite;
• crée une suite de suites et lance la campagne.

9.6 La documentation des sources


Durant la vie d’un projet, on distingue plusieurs types de documentation :
• les documents de spécification (ensemble explicite d’exigences à satisfaire) ;
• les documents techniques attachés au code ;
• les manuels d’utilisation et autres documents de haut niveau.
Les documents techniques évoluent au rythme du code et peuvent donc être traités
comme lui : ils doivent pouvoir être lus et manipulés avec un simple éditeur de texte.
Il existe deux outils majeurs pour concevoir des documents pour les applications Python :
reStructuredText (ou reST) : un format enrichi ;
les doctests : compatibles avec le format reST. Ils permettent de combiner les textes applicatifs
avec les tests.

9.6.1 Le format reST


Le format reStructuredText, communément appelé reST est un système de balises utilisé pour
formater des textes.
À la différence de LATEX ou d’HTML il enrichit le document de manière « non intrusive »,
c’est-à-dire que les fichiers restent directement lisibles.

docutils
Le projet docutils, qui inclut l’interpréteur reST, fournit un jeu d’utilitaires :
rst2html génère un rendu html avec une feuille de style css intégrée ;
rst2latex crée un fichier LATEX équivalent ;
rst2s5 construit une présentation au format s5, qui permet de créer des présentations interactives
en
HTML.

Sphinx
Sphinx est un logiciel libre de type générateur de documentation. Il s’appuie sur des fichiers au
format reStructuredText, qu’il convertit en HTML, PDF, man, et autres formats.
De nombreux projets utilisent Sphinx pour leur documentation officielle, tels que Python, Django,
Selenium, Urwid, ou encore Bazaar.
84 Techniques avancées

rst2pdf
Par ailleurs, le programme rst2pdf génère directement une documentation au format
PDF. Voici un exemple 1 simple de fichier texte au format reST.
On remarque entre autres que :
• la principale balise est la ligne blan e qui sépare les différentes structures du texte ;
• la structuration se fait en soulignant les titres des sections de différents niveaux avec des
caractères de ponctuation (= - _ : , etc.). À chaque fois qu’il rencontre un texte ainsi souligné,
l’interpréteur associe le caractère utilisé à un niveau de section ;
• un titre est généralement souligné et surligné avec le même caractère, comme dans
l’exemple suiv- ant :

======================
Fichier au format reST
======================

Section 1
=========

On est dans la section 1.

Sous-section
::::::::::::

Ceci est une sous-section.

Sous-sous-section
.................

Ceci est une sous-sous-section.

.. et ceci un commentaire

Section 2
=========

La section 2 est ``beaucoup plus`` **intéressante** que la section 1.

Section 3
=========

La section 2 est un peu vantarde : la section 1 est *très bien*.

Une image au format "png"


:::::::::::::::::::::::::

.. figure :: [Link]
:scale : 50%

L’utilitaire rst2pdf, appliqué à ce fichier, produit le fichier de même nom (☞ Fig. 9.6) mais avec
l’ex- tension .pdf.

9.6.2 Le module doctest

Le principe du literate programming (ou programmation littéraire) de Donald K consiste à mêler


dans le source le code et la documentation du programme.
Ce principe été repris en Python pour documenter les API via les chaînes de documentation
(docstring). Des programmes comme Epydocpeuvent alors les extraire des modules pour composer
une documentation séparée.
Il est possible d’aller plus loin et d’inclure dans les chaînes de documentation des exemples
d’utilisation, écrits sous la forme de session interactive.
Examinons deux exemples.
1. cf. [7],
9.6 La documentation des sources 85

Pour chacun, nous donnerons d’une part le source muni de sa chaîne de documentation dans
lequel le module standard doctest permet d’extraire puis de lancer ces sessions pour vérifier
qu’elles fonctionnent et, d’autre part un résultat de l’exécution.

Premier exemple : [Link]

# -*- coding : utf-8 -*-


"""Module d'essai de doctest."""

import doctest

def somme(a, b) :
"""Renvoie a + b.

>>> somme(2, 2)

>>> somme(2, 4)
6
"""
return a+b

if name == ' main ' :


print("{ :-^40}".format(" Mode silencieux "))
[Link]()
print("Si tout va bien, on a rien vu !")

print("\n{ :-^40}".format(" Mode détaillé "))


[Link](verbose=True)

L’exécution de ce fichier donne :


Mode silencieux
Si tout va bien, on a rien vu !

Mode détaillé
Trying :
somme(2, 2)
Expecting :

ok
Trying :
somme(2, 4)
Expecting :

ok
1 items had no tests :
main
items passed all tests :
2 tests in main .somme
tests in 2 items.
2 passed and 0 failed.
Test passed.

Deuxième exemple : [Link]

# -*- coding : UTF-8 -*-


"""Module d'essai de doctest."""

# fonctions
def accentEtrange(texte) :
"""Ajoute un accent étrange à un texte.

Les 'r' sont Triplés, les 'e' suivi d'un 'u'

Exemple :

>>> texte = "Est-ce que tu as regardé la télé hier soir ? Il y avait un théma sur les ramasseurs d'escargots en
Laponie, ils en bavent..."
>>> accentEtrange(texte)
86 Techniques avancées

Est-ceu queu tu as rRreugarRrdé la télé hieurRr soirRr ? Il y avait un théma surRr leus rRramasseuurRrs d'
euscarRrgots eun Laponieu, ils eun baveunt...

Cette technique permet d'internationnaliser les applications


pour les rendre compatibles avec certaines régions françaises.
"""
texte = [Link]('r', 'rRr')
print([Link]('e', 'eu'))

def _test() :
import doctest
[Link](verbose=True)

if name == ' main ' :


_test()

L’exécution de ce fichier donne :


Trying :
texte = "Est-ce que tu as regardé la télé hier soir ? Il y avait un théma sur les ramasseurs d'escargots en
Laponie, ils en bavent..."
Expecting nothing
ok
Trying :
accentEtrange(texte)
Expecting :
Est-ceu queu tu as rRreugarRrdé la télé hieurRr soirRr ? Il y avait un théma surRr leus rRramasseuurRrs d'
euscarRrgots eun Laponieu, ils eun baveunt...
ok
2 items had no tests :
main
main ._test
items passed all tests :
2 tests in main .accentEtrange
tests in 3 items.
2 passed and 0 failed.
Test passed.

9.6.3 Le développement dirigé par la documentation


Comme on peut le voir, la documentation intégrée présente néanmoins un défaut : quand la
documentation augmente, on ne voit plus le code !
La solution est de déporter cette documentation : la fonction [Link]() permet d’indiquer le
nom du fichier de documentation.
i plus est, on peut écrire ce fichier au format reST, ce qui permet de faire coup double. D’une
part, on dispose des tests intégrés à la fonction (ou à la méthode) et, d’autre part, le même
fichier fournit une documentation à jour.

Exemple : test_documentation2.py
Fichier de documentation 1 :
Le module ``accent``
====================

Test de la fonction ``accentEtrange``

Ce module fournit une fonction ``accentEtrange``.


On peut ainsi ajouter un accent à un texte :

>>> from doctest2 import accentEtrange


>>> texte = "Est-ce que tu as regardé la télé hier soir ? Il y avait un théma sur
les ramasseurs d'escargots en Laponie, ils en bavent..."
>>> accentEtrange(texte)
Est-ceu queu tu as rRreugarRrdé la télé hieurRr soirRr ? Il y avait un théma surRr
1. cf. [7], p. 123.
9.6 La documentation des sources 87

leus rRramasseuurRrs d'euscarRrgots eun Laponieu, ils eun baveunt...

Les ``r`` sont triplés et les ``e`` épaulés par des ``u``. Cette technique permet
de se passer de systèmes de traductions complexes pour faire fonctionner
les logiciels dans certaines régions.

Source du module :
import doctest
[Link]("test_documentation2.txt", verbose=True)

Nous produisons la documentation HTML par la commande :


rst2html test_documentation2.txt test_documentation2.html

Elle est illustrée ☞ Fig. 9.7.


88 Techniques avancées

F 9.6 – Exemple de sortie au format PDF.


9.6 La documentation des sources 89

Figure 9.7 – Documentation du script test_documentation2.py.


ANNEXE B

Passer du problème au programme

Lorsqu’on a un problème à résoudre par un programme, la difficulté est de savoir :


Par où commencer ?
Comment concevoir l’algorithme ?

Prérequis
Au fur et à mesure que l’on acquière de l’expérience, on découvre et on apprend à utiliser les
bibliothèques de modules et paquets qui fournissent des types de données et des services
avancés, évitant d’avoir à re- créer, coder et déboguer une partie de la solution.

Réutiliser
La première chose à faire est de vérifier qu’il n’existe pas déjà une solution (même partielle) au
problème que l’on pourrait reprendre in extenso ou dont on pourrait s’inspirer. On peut chercher
dans les nombreux modules standard installés avec le langage, dans les dépôts institutionnels
de modules tiers (le Python Package Index 1 par exemple), ou encore utiliser les moteurs de
recherche sur l’Internet. Si on ne trouve pas de solution existante dans notre langage préféré, on
peut trouver une solution dans un autre langage, qu’il n’y aura plus qu’à adapter.
L’analyse qui permet de créer un algorithme et la programmation ensuite, sont deux
phases qui né- cessitent de la pratique avant de devenir « évidentes » ou « faciles ».

Réfléchir à un algorithme
Pour démarrer, il faut partir d’éléments réels, mais sur un échantillon du problème comportant
peu de données, un cas que l’on est capable de traiter « à la main ».
Il est fortement conseillé de démarrer sur papier ou sur un tableau (le papier ayant l’avantage
de laisser plus facilement des traces des différentes étapes).
On identifie tout d’abord quelles sont les données que l’on a à traiter en entrée et quelles
sont les données que l’on s’attend à trouver en sortie. Pour chaque donnée, on essaie de
préciser quel est son domaine, quelles sont ses limites, quelles contraintes la lient aux autres
données.

Résoudre « à la main »
On commence par une résolution du problème, en réalisant les transformations et calculs sur
notre échan- tillon de problème, en fonctionnant par étapes.
À chaque étape, on note :
• quelles sont les étapes pertinentes, sur quels critères elles ont été choisies ;
• quelles sont les séquences d’opérations que l’on a répété.

Lorsque l’on tombe sur des étapes complexes, on découpe en sous-étapes, éventuellement
en les trai- tant séparément comme un algorithme de résolution d’un sous-problème. Le but est
d’arriver à un niveau de détails suffisamment simple ; soit qu’il s’agisse d’opérations très basiques
(opération sur un texte, ex- pression de calcul numérique…), soit que l’on pense/sache qu’il existe
déjà un outil pour traiter ce sous- problème (calcul de sinus pour un angle, opération de tri sur
une séquence de données. . .).
Lors de ce découpage, il faut éviter de considérer des opérations comme « implicites » ou «
évidentes », il faut préciser d’où proviennent les informations et ce que l’on fait des résultats.
Par exemple on ne
1. [Link]
96 Passer du problème au
programme

considère pas « un élément » mais « le nom traité est l’élément suivant de la séquence de noms
» ou encore « le nom traité est le xe élément de la séquence de noms ».
Normalement, au cours de ces opérations, on a commencé à nommer les données et les
étapes au fur et à mesure qu’on en a eu besoins.

Formaliser
Une fois qu’on a un brouillon des étapes, il faut commencer à mettre en forme et à identifier les
construc- tions algorithmiques connues et les données manipulées :

• Boucles (sur quelles informations, condition d’arrêt).


• Tests (quelle condition).
• Informations en entrée, quel est leur type, quelles sont les contraintes pour qu’elles
soient valides et utilisables, d’où viennent-elles :
– déjà présentes en mémoire,
– demandées à l’utilisateur,
– lues dans des fichiers ou récupérées ailleurs (sur l’Internet par exemple).
• Calculs et expressions :
– quel genre de données sont nécessaires, y-a-t-il des éléments constants à connaître,
des résultats intermédiaires à réutiliser,
– on peut identifier ici les contrôles intermédiaires possibles sur les valeurs qui puissent
permettre de vérifier que l’algorithme se déroule bien.
• Stockage des résultats intermédiaires.
• Résultat final — à quel moment l’a-t-on, qu’en fait-on :
– retourné dans le cadre d’une fonction,
– affiché à l’utilisateur,
– sauvegardé dans un fichier.

Factoriser
Le but est d’identifier les séquences d’étapes qui se répètent en différents endroits, séquences
qui seront de bons candidats pour devenir des fonctions ou des classes. Ceci peut être fait en
même temps que l’on formalise.

Passer de l’idée au programme


Le passage de l’idée puis de l’algorithme au code dans un programme, est relativement facile en
Python car celui-ci est très proche d’un langage d’algorithmique.

• Les noms des choses que l’on a manipulé vont nous donner des variables.
• Les tests vont se transformer en if condition:
• Les boucles sur des séquences d’informations vont se transformer en for variable in séquence:
• Les boucles avec expression de condition vont se transformer en while conditions:
• Les séquences d’instructions qui se répètent en différents endroits vont se transformer en
fonc-tions.
• Le retour de résultat d’une séquence (fonction) va se traduire en return variable.
• Les conditions sur les données nécessaires pour un traitement vont identifier des tests
d’erreurs et des levées d’exception.
A C

Jeux de caractères et encodage

Position du problème
Nous avons vu que l’ordinateur code toutes les informations qu’il manipule en binaire. Pour coder
les nombres entiers un changement de base suffit, pour les flottants, on utilise une norme (IEEE
754), mais la situation est plus complexe pour représenter les caractères.
En effet, la grande diversité des langues humaines et de leur représentation nécessite un codage
adapté.

La première idée est de construire une table qui associe les symboles à représenter à
un nombre (généralement codé sur un octet) :
Symbole ←→ Nombre
La plus célèbre est la table ASCII 1 (☞ Fig. C.1), codée sur 7 bits (soit 128 codes), mais bien
d’autres tables ont été créées (EBCDIC, ISO-8852-1. . .).

Figure C.1 – Table ASCII.

La table Unicode
En Python 3, les chaînes de caractères (le type str()) sont des chaînes Unicode, norme dans
laquelle les identifiants numériques de leurs caractères sont uniques et universels.
Comme il s’agit de différencier plusieurs centaines de milliers de caractères (on compte
environ 6000 langues dans le monde) ils ne pourront évidemment pas être encodés sur un seul
octet.
En fait, la norme Unicode ne fixe aucune règle concernant le nombre d’octets ou de bits à
réserver pour l’encodage, mais spécifie seulement la valeur numérique de l’identifiant associé à
chaque caractère (☞ Fig. C.2).
1. American Standard Code for Information Interchange
98 Jeux de caractères et
encodage

Figure C.2 – Extrait de la table Unicode.

Encodage
Après avoir collecté tous les symboles et y avoir associé un nombre, il reste à leur trouver une
représenta- tion binaire.
Pour l’ASCII un seul octet suffisait mais pour représenter les millions de possibilités de
l’Unicode, plusieurs octets par caractère sont nécessaires.

Comme la plupart des textes n’utilisent que la table ASCII ce qui correspond justement à
la partie basse de la table Unicode, l’encodage le plus économique est l’UTF-8 1 :
• pour les codes 0 à 127, l’UTF-8 utilise l’octet de la table ASCII ;
• pour les caractères spéciaux (codes 128 à 2047), l’UTF-8 utilise 2 octets ;
• pour les caractères spéciaux encore moins courants (codes 2048 à 65535), l’UTF-8 utilise 3
octets ;
• enfin pour les autres, l’UTF-8 en utilise 4.

Exemple de l’encodage UTF-8 du caractère Unicode « é » :

Symbol Code Code Encodage UTF-


e décimal hexadécimal 8
é 233 e9 C3 A9

En utilisant la notation hexadécimale et le caractère d’échappement de la tabulation, on


obtient par exemple :
>>> print("\u00e9 \t \u0110 \t \u0152")
é Đ Œ

Applications aux scripts Python


Puisque les scripts Python que l’on produit avec un éditeur sont eux-mêmes des textes, ils
risquent d’être encodés suivant différentes format. Afin que Python utilise le bon, il est important
de précisez l’encodage de caractères utilisé 2 (obligatoirement en 1re ou 2e ligne des sources).

Les encodages les plus fréquents sont 3 :


# -*- coding : latin1 -*-

ou :
# -*- coding : utf8 -*-

1. Retour chap. 2, p. 5
2. appelé codec.
3. Notons que latin1et utf8sont des alias de latin-1et utf-8.
ANNEXE D

Les bases arithmétiques

Définition
Définition
En arithmétique, une base n désigne la valeur dont les puissances successives interviennent
dans l’écriture des nombres, ces puissances définissant l’ordre de grandeur de chacune des
positions occupées par les chiffres composant tout nombre. Par exemple : 57n = (5 × n1) + (7
× n0)
Certaines bases sont couramment employées :
• la base 2 (système binaire), en électronique numérique et informatique ;
• la base 8 (système octal), en informatique ;
• la base 16 (système hexadécimal), fréquente en informatique ;
• la base 60 (système sexagésimal), dans la mesure du temps et des angles.

Conversion
Définition
Les changements de base : un nombre dans une base n donnée s’écrit sous la forme d’addition des
puissances successives de cette base 1.

Exemples

5716 = (5 × 161) + (7 × 160) = 8710


578 = (5 × 81) + (7 × 80) = 4710

1. Retour chap.2, p. 7
Annexe E

Exercices corrigés

Énoncés des exercices


Remarque
✓ Les exercices suivants sont fournis à titre d’exemples et de modèles.
Ils sont soit simples, soit moins simples (notés ✠ dans la marge) soit plus difficiles (notés ✠✠).

1. Écrire un programme qui, à partir de la saisie d’un rayon et d’une hauteur, calcule le
volume d’un cône droit.

2. Une boucle while : entrez un prix HT (entrez 0 pour terminer) et affichez sa valeur TTC.

3. Une autre boucle while : calculez la somme d’une suite de nombres positifs ou nuls.
Comptez com- bien il y avait de données et combien étaient supérieures à 100.
Entrer un nombre inférieur ou égal à 0 indique la fin de la suite.

4. L’utilisateur donne un entier positif n et le programme affiche PAIR s’il est divisible par 2, IMPAIR
sinon.

5. L’utilisateur donne un entier positif et le programme annonce combien de fois de suite


cet entier est divisible par 2.

6. L’utilisateur donne un entier supérieur à 1 et le programme affiche, s’il y en a, tous ses


diviseurs ✠propres sans répétition ainsi que leur nombre. S’il n’y en a pas, il indique qu’il
est premier. Par exemple :
Entrez un entier strictement positif 12
Diviseurs propres sans répétition de 12 : 2 3 4 6 (soit 4 diviseurs propres)

Entrez un entier strictement positif 13


Diviseurs propres sans répétition de 13 : aucun ! Il est premier

7. Écrire un programme qui approxime par défaut la valeur de la constante mathématique e, pour
n
assez grand 1, en utilisant la formule :
∑ n1
e ≈
i!
i=0

Pour cela, définissez la fonction factorielle et, dans votre programme principal, saisissez
l’ordre n et affichez l’approximation correspondante de e.

8. Un gardien de phare va aux toilettes cinq fois par jour or les WC sont au rez-de-chaussée.. .
Écrire une procédure (donc sans return) hauteurParcourue qui reçoit deux paramètres, le
nombre de marches du phare et la hauteur de chaque marche (en cm), et qui affiche :
Pour x marches de y cm, il parcourt [Link] m par semaine.

On n’oubliera pas :
• qu’une semaine comporte 7 jours ;
• qu’une fois en bas, le gardien doit remonter ;
• que le résultat est à exprimer en m.

1. Mais pas trop pour éviter le dépassement de capacité : OverflowError : long int too large to convert to float.
102 Exercices corrigés

9. Un permis de chasse à points remplace désormais le permis de chasse traditionnel. Chaque


chasseur possède au départ un capital de 100 points. S’il tue une poule il perd 1 point, 3
points pour un chien, 5 points pour une vache et 10 points pour un ami. Le permis coûte 200
euros.
Écrire une fonction amende qui reçoit le nombre de victimes du chasseur et qui renvoie la
somme due.
Utilisez cette fonction dans un programme principal qui saisit le nombre de victimes et qui
affiche la somme que le chasseur doit débourser.

10. Je suis ligoté sur les rails en gare d’Arras. Écrire un programme qui affiche un tableau me
permettant de connaître l’heure à laquelle je serai déchiqueté par le train parti de la gare du
Nord à 9h (il y a 170 km entre la gare du Nord et Arras).
Le tableau prédira les différentes heures possibles pour toutes les vitesses de 100 km/h à
300 km/h, par pas de 10 km/h, les résultats étant arrondis à la minute inférieure.
– Écrire une procédure tchacatchac qui reçoit la vitesse du train et qui affiche l’heure du drame
;
– écrire le programme principal qui affiche le tableau demandé.
✠ 11. Un programme principal saisit une chaîne d’ADN valide et une séquence d’ADN valide («
valide » signifie qu’elles ne sont pas vides et sont formées exclusivement d’une
combinaison arbitraire de
"a", "t", "g" ou "c").
Écrire une fonction validequi renvoie vrai si la saisie est valide, faux sinon.
Écrire une fonction saisiequi effectue une saisie valide et renvoie la valeur saisie sous forme
d’une chaîne de caractères.
Écrire une fonction proportion qui reçoit deux arguments, la chaîne et la séquence et qui
retourne la proportion de séquence dans la chaîne (c’est-à-dire son nombre
d’occurrences).
Le programme principal appelle la fonction saisie pour la chaîne et pour la séquence et
affiche le résultat.
Exemple d’affichage :
chaîne : attgcaatggtggtacatg
séquence : ca
Il y a 10.53 % de "ca" dans votre chaîne.

12. Il s’agit d’écrire, d’une part, un programme principal et, d’autre part, une fonction utilisée
dans le programme principal.
La fonction listAleaInt(n, a, b)retourne une liste de nentiers aléatoires dans [a .. b]en utilisant la
fonction randint(a, b)du module random.
Dans le programme principal :
• construire la liste en appelant la fonction listAleaInt();
• calculer l’index de la case qui contient le minimum ;
• échangez le premier élément du tableau avec son minimum.

13. Comme précédemment, il s’agit d’écrire, d’une part, un programme principal et, d’autre
part, une fonction utilisée dans le programme principal.
La fonction listAleaFloat(n) retourne une liste de n flottants aléatoires en utilisant la fonction
random()du module random.
Dans le programme principal :
• Saisir un entier ndans l’intervalle : [2 .. 100];
• construire la liste en appelant la fonction listAleaFloat();
• afficher l’amplitude du tabeau (écart entre sa plus grande et sa plus petite valeur) ;
• afficher la moyenne du tableau.

14. Fonction renvoyant plusieurs valeurs sous forme d’un tuple.


Écrire une fonction minMaxMoy() qui reçoit une liste d’entiers et qui renvoie le minimum, le
maximum et la moyenne de cette liste. Le programme principal appellera cette fonction avec
la liste : [10, 18,14, 20, 12, 16].

15. Saisir un entier entre 1 et 3999 (pourquoi cette limitation ?). L’afficher en nombre romain.

✠ 16. Améliorer le script précédent en utilisant la fonction zip().


Exercices corrigés 103

✠ 17. Un tableau contient n entiers (2 < n < 100) aléatoires tous compris entre 0 et 500.
Vérifier qu’ils sont tous différents.

18. Proposer une autre version très simple du problème précédent en comparant les
longueurs des ✠tabeaux avant et après traitement, le traitement consiste à utiliser une
structure de données con- tenant des éléments uniques.

19. L’utilisateur donne un entier n entre 2 et 12, le programme donne le nombre de façons de
faire n en lançant deux dés.

20. Même problème que le précédent mais avec n entre 3 et 18 et trois dés.

21. Généralisation des deux questions précédentes. L’utilisateur saisit deux entrées, d’une part le nom-
✠✠
bre de dés, nbd (que l’on limitera pratiquement à 10) et, d’autre part la somme, s, comprise
entre
nbd et [Link]. Le programme calcule et affiche le nombre de façons de faire s avec les nbd dés.

22. Même problème que le précédent mais codé récursivement. ✠✠


23. Nombres parfaits et nombres chanceux. ✠
• On appelle nombre premier tout entier naturel supérieur à 1 qui possède exactement deux
di-
viseurs, lui-même et l’unité.
• On appelle diviseur propre de n, un diviseur quelconque de n, n exclu.
• Un entier naturel est dit parfait s’il est égal à la somme de tous ses diviseurs propres.
• Un entier ntel que : (n + i + i2) est premier pour tout idans [0, n − 2] est dit chanceux.
Écrire un module (parfait_chanceux_m.py) définissant quatre fonctions : somDiv, estParfait, est-
Premier, estChanceuxet un auto-test :

• la fonction somDiv retourne la somme des diviseurs propres de son argument ;


• les trois autres fonctions vérifient la propriété donnée par leur définition et retourne un
booléen. Plus précisément, si par exemple la fonction estPremier vérifie que son argument
est premier, elle retourne True, sinon elle retourne False.
La partie de test doit comporter quatre appels à la fonction verif permettant de tester somDiv(12),
estParfait(6), estPremier(31) et estChanceux(11).

# -*- coding : utf-8 -*-


"""Module de vérification."""
# fichier : verif_m.py
# auteur : Bob Cordeau

# import
from sys import exit

# définition de fonction
def verif(entree, reference, precision=0, comment="") :
"""Vérifie que <entree> est égal à <référence>, à la précision près."""
print("{} [{}, {}]".format(comment, entree, reference), end=" ")
if abs(entree - reference) <= precision :
print(" *** ok")
else :
print(" ### ko")

# Auto-test ===================================================================
if name ==' main ' :
verif(abs(-6/2), 3, comment="\nTeste la fonction 'abs' : ")

pi = 3.142
print("\npi = {}".format(pi))
verif(pi, 3.14, 1e-2, comment="à 1e-2 :") # Ok
verif(pi, 3.14, 1e-3, comment="à 1e-3 :") # Erreur

Puis écrire le programme principal (parfait_chanceux.py) qui comporte :


– l’initialisation de deux listes : parfaitset chanceux;
104 Exercices corrigés

– une boucle de parcours de l’intervalle [2, 1000] incluant les tests nécessaires pour
remplir ces listes ;
– enfin l’affichage de ces listes.

Solutions des exercices

# -*- coding : utf8 -*-


"""Volume d'un cône droit."""

# import
from math import pi

# programme principal =========================================================


rayon = float(input("Rayon du cône (m) : "))
hauteur = float(input("Hauteur du cône (m) : "))

volume = (pi*rayon*rayon*hauteur)/3.0
print("Volume du cône =", volume, "m3")

# -*- coding : utf8 -*-


"""Calcul d'un prix TTC."""

# programme principal =========================================================


prixHT = float(input("Prix HT (0 pour terminer) ? "))
while prixHT > 0:
print("Prix TTC : { :.2f}\n".format(prixHT * 1.196))
prixHT = float(input("Prix HT (0 pour terminer) ? "))

print("Au revoir !")

# -*- coding : utf8 -*-


"""Somme d'entiers et nombre d'entiers supérieur à 100."""

# programme principal =========================================================


somme, nombreTotal, nombreGrands = 0, 0, 0

x = int(input("x (0 pour terminer) ? "))


while x > 0:
somme += x
nombreTotal += 1
if x > 100:
nombreGrands += 1
x = int(input("x (0 pour terminer) ? "))

print("\nSomme :", somme)


print(nombreTotal, "valeur(s) en tout, dont", nombreGrands,
"supérieure(s) à 100")

# -*- coding : utf8 -*-


"""Parité."""

# programme principal =========================================================


n = int(input("Entrez un entier strictement positif : "))
while n < 1:
n = int(input("Entrez un entier STRICTEMENT POSITIF, s.v.p. : "))

if n%2 == 0:
print(n, "est pair.")
else :
Exercices corrigés 105

print(n, "est impair.")

# -*- coding : utf8 -*-


"""Nombre de fois qu'un entier est divisible par 2."""

# programme principal =========================================================


n = int(input("Entrez un entier strictement positif : "))
while n < 1:
n = int(input("Entrez un entier STRICTEMENT POSITIF, s.v.p. : "))
save = n

cpt = 0
while n%2 == 0:
n /= 2
cpt += 1

print(save, "est", cpt, "fois divisible par 2.")

# -*- coding : utf8 -*-


"""Diviseurs propres d'un entier."""

# programme principal =========================================================


n = int(input("Entrez un entier strictement positif : "))
while n < 1:
n = int(input("Entrez un entier STRICTEMENT POSITIF, s.v.p. : "))

i = 2 # plus petit diviseur possible de n


cpt = 0 # initialise le compteur de divisions
p = n/2 # calculé une fois dans la boucle

print("Diviseurs propres sans répétition de ", n, " :", end=' ')


while i <= p :
if n%i == 0:
cpt += 1
print(i, end=' ')
i += 1

if not cpt :
print("aucun ! Il est premier.")
else :
print("(soit", cpt, "diviseurs propres)")

# -*- coding : utf8 -*-


"""Approximation de 'e'."""

# définition de fonction
def fact(n) :
r = 1
for i in range(1, n+1) :
r *= i
return r

# programme principal =========================================================


n = int(input("n ? "))
exp = 0.0
for i in range(n) :
exp = exp + 1.0/fact(i)

print("Approximation de 'e' : { :.3f}".format(exp))

# -*- coding : utf8 -*-


"""Gardien de phare."""

# définition de fonction
def hauteurParcourue(nb, h) :
print("Pour { :d} marches de { :d} cm, il parcourt { :.2f} m par semaine !"
.format(nb, h, nb*h*2*5*7/100.0))
106 Exercices corrigés

# programme principal =========================================================


nbMarches = int(input("Combien de marches ? "))
hauteurMarche = int(input("Hauteur d'une marche (cm) ? "))

hauteurParcourue(nbMarches, hauteurMarche)

# -*- coding : utf8 -*-


"""Permis de chasse."""

# définition de fonction
def permisSup(p, c, v, a) :
pointsPerdus = p + 3*c + 5*v + 10*a
nbrePermis = pointsPerdus/100.0
return 200*nbrePermis

# programme principal =========================================================


poules = int(input("Combien de poules ? "))
chiens = int(input("Combien de chiens ? "))
vaches = int(input("Combien de vaches ? "))
amis = int(input("Combien d'amis ? "))

payer = permisSup(poules, chiens, vaches, amis)

print("\nA payer :", end=' ')


if payer == 0:
print("rien à payer")
else :
print(payer, "euros")

# -*- coding : utf8 -*-


"""Histoire de train."""

# définition de fonction
def tchacatchac(v) :
"""Affiche l'heure du drame."""
heure = 9 + int(170/v)
minute = (60 * 170 / v) % 60
print("A { :>3} km/h, je me fais déchiqueter à { :>2} h { :.2f} min."
.format(v, heure, minute))

# programme principal =========================================================


i = 100
while i <= 300:
tchacatchac(i)
i += 10

# -*- coding : utf8 -*-


"""Proportion d'une séquence dans une chaîne d'ADN."""

# définition de fonction
def valide(seq) :
"""Retourne vrai si la séquence est valide, faux sinon."""
ret = any(seq)
for c in seq :
ret = ret and c in "atgc"
return ret

def proportion(a, s) :
"""Retourne la proportion de la séquence <s> dans la chaîne <a>."""
return 100*[Link](s)/len(a)

def saisie(ch) :
s = input("{ :s} : ".format(ch))
while not valide(s) :
print("'{ :s}' ne peut contenir que les chaînons 'a', 't', 'g' et 'c'"
Exercices corrigés 107

" et ne doit pas être vide".format(ch))


s = input("{ :s} : ".format(ch))
return s

# programme principal =========================================================


adn = saisie("chaîne")
seq = saisie("séquence")

print('Il y a { :.2f} % de "{ :s}" dans votre chaîne.'


.format(proportion(adn, seq), seq))

# -*- coding : utf8 -*-


"""Echanges."""

# import
from random import seed, randint

# définition de fonction
def listAleaInt(n, a, b) :
"""Retourne une liste de <n> entiers aléatoires dans [<a> .. <b>]."""
return [randint(a, b) for i in range(n)]

# programme principal =========================================================


seed() # initialise le générateur de nombres aléatoires
t = listAleaInt(100, 2, 125) # construction de la liste

iMin = [Link](min(t)) # calcule l'index du minimum de la liste

print("Avant échange :")


print("\tt[0] =", t[0], "\tt[iMin] =", t[iMin])
t[0], t[iMin] = t[iMin], t[0] # échange
print("Apres échange :")
print("\tt[0] =", t[0], "\tt[iMin] =", t[iMin])

# -*- coding : utf8 -*-


"""Amplitude et moyenne d'une liste de flottants."""

# import
from random import seed, random

# définition de fonction
def listAleaFloat(n) :
"Retourne une liste de <n> flottants aléatoires"
return [random() for i in range(n)]

# programme principal =========================================================


n = int(input("Entrez un entier [2 .. 100] : "))
while not(n >= 2 and n <= 100) :
n = int(input("Entrez un entier [2 .. 100], s.v.p. : "))

seed() # initialise le générateur de nombres aléatoires


t = listAleaFloat(n) # construction de la liste

print("Amplitude : { :.2f}".format(max(t) - min(t)))


print("Moyenne : { :.2f}".format(sum(t)/n))

# -*- coding : utf8 -*-


"""Min, max et moyenne d'une liste d'entiers."""

# définition de fonction
def minMaxMoy(liste) :
"""Renvoie le min, le max et la moyenne de la liste."""
min, max, som = liste[0], liste[0], float(liste[0])
for i in liste[1:]:
if i < min :
108 Exercices corrigés

min = i
if i > max :
max = i
som += i
return (min, max, som/len(liste))

# programme principal =========================================================


lp = [10, 18, 14, 20, 12, 16]

print("liste =", lp)


l = minMaxMoy(lp)
print("min : {0[0]}, max : {0[1]}, moy : {0[2]}".format(l))

# -*- coding : utf8 -*-


"""Nombres romains (version 1)."""

# programme principal =========================================================


n = int(input('Entrez un entier [1 .. 4000[ : '))
while not(n >= 1 and n < 4000) :
n = int(input('Entrez un entier [1 .. 4000[, s.v.p. : '))

s = "" # Chaîne résultante

while n >= 1000:


s += "M"
n -= 1000

if n >= 900:
s += "CM"
n -= 900

if n >= 500:
s += "D"
n -= 500

if n >= 400:
s += "CD"
n -= 400

while n >= 100:


s += "C"
n -= 100

if n >= 90:
s += "XC"
n -= 90

if n >= 50:
s += "L"
n -= 50

if n >= 40:
s += "XL"
n -= 40

while n >= 10:


s += "X"
n -= 10

if n >= 9:
s += "IX"
n -= 9

if n >= 5:
s += "V"
n -= 5

if n >= 4:
s += "IV"
n -= 4

while n >= 1:
Exercices corrigés 109

s += "I"
n -= 1

print("En romain :", s)

# -*- coding : utf8 -*-


"""Nombres romains (version 2)."""

# globales
CODE = zip(
[1000,900 ,500,400 ,100,90 ,50 ,40 ,10 ,9 ,5 ,4 ,1],
["M" ,"CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"]

# définition de fonction
def decToRoman(num) :
res = []
for d, r in CODE :
while num >= d :
[Link](r)
num -= d
return ''.join(res)

# programme principal =========================================================


for i in range(1, 4000) :
print(i, decToRoman(i))

# -*- coding : utf8 -*-


"""Liste d'entiers différents."""

# import
from random import seed, randint

# définition de fonction
def listAleaInt(n, a, b) :
"""Retourne une liste de <n> entiers aléatoires entre <a> et <b>."""
return [randint(a, b) for i in range(n)]

# programme principal =========================================================


n = int(input("Entrez un entier [1 .. 100] : "))
while not(n >= 1 and n <= 100) :
n = int(input("Entrez un entier [1 .. 100], s.v.p. : "))

# construction de la liste
seed() # initialise le générateur de nombres aléatoires
t = listAleaInt(n, 0, 500)

# Sont-ils différents ?
tousDiff = True
i = 0
while tousDiff and i < (n-1) :
j = i + 1
while tousDiff and j < n :
if t[i] == t[j]:
tousDiff = False
else :
j += 1
i += 1

if tousDiff :
print("\nTous les éléments sont distincts.")
else :
print("\nAu moins une valeur est répétée.")
print(t)

# -*- coding : utf8 -*-


"""Liste d'entiers différents (seconde version)."""
110 Exercices corrigés

# import
from random import seed, randint

# définition de fonction
def listAleaInt(n, a, b) :
"""Retourne une liste de <n> entiers aléatoires entre <a> et <b>."""
return [randint(a, b) for i in range(n)]

# programme principal =========================================================


n = int(input("Entrez un entier [1 .. 100] : "))
while not(n >= 1 and n <= 100) :
n = int(input("Entrez un entier [1 .. 100], s.v.p. : "))

seed() # initialise le générateur de nombres aléatoires


avant = listAleaInt(n, 0, 500)
apres = list(set(avant))

if len(avant) == len(apres) :
print("\nTous les éléments sont distincts.")
else :
print("\nAu moins une valeur est répétée.")
print(avant)

# -*- coding : utf8 -*-


"""Jeu de dés (1)."""

# programme principal =========================================================


n = int(input("Entrez un entier [2 .. 12] : "))
while not(n >= 2 and n <= 12) :
n = int(input("Entrez un entier [2 .. 12], s.v.p. : "))

s = 0
for i in range(1, 7) :
for j in range(1, 7) :
if i+j == n :
s += 1

print("Il y a { :d} façon(s) de faire { :d} avec deux dés.".format(s, n))

# -*- coding : utf8 -*-


"""Jeu de dés (2)."""

# programme principal =========================================================


n = int(input("Entrez un entier [3 .. 18] : "))
while not(n >= 3 and n <= 18) :
n = int(input("Entrez un entier [3 .. 18], s.v.p. : "))

s = 0
for i in range(1, 7) :
for j in range(1, 7) :
for k in range(1, 7) :
if i+j+k == n :
s += 1

print("Il y a { :d} façon(s) de faire { :d} avec trois dés.".format(s, n))

# -*- coding : utf8 -*-


"""Jeu de dés (3)."""

# globale
MAX = 8

# programme principal =========================================================


nbd = int(input("Nombre de dés [2 .. { :d}] : ".format(MAX)))
while not(nbd >= 2and nbd <= MAX) :
Exercices corrigés 111

nbd = int(input("Nombre de dés [2 .. { :d}], s.v.p. : ".format(MAX)))

s = int(input("Entrez un entier [{ :d} .. { :d}] : ".format(nbd, 6*nbd)))


while not(s >= nbd and s <= 6*nbd) :
s = int(input("Entrez un entier [{ :d} .. { :d}], s.v.p. : ".format(nbd, 6*nbd)))

if s == nbd or s == 6*nbd :
cpt = 1 # 1 seule solution
else :
I = [1]*nbd # initialise une liste de <nbd> dés
cpt, j = 0, 0
while j < nbd :
som = sum([I[k] for k in range(nbd)])

if som == s :
cpt += 1 # compteur de bonnes solutions
if som == 6*nbd :
break

j = 0
if I[j] < 6:
I[j] += 1
else :
while I[j] == 6:
I[j] = 1
j += 1
I[j] += 1

print("Il y a { :d} façons de faire { :d} avec { :d} dés.".format(cpt, s, nbd))

# -*- coding : utf8 -*-


"""Jeu de dés (récursif)."""

# globale
MAX = 8

# définition de fonction
def calcul(d, n) :
"""Calcul récursif du nombre de façons de faire <n> avec <d> dés."""
resultat, debut = 0, 1
if (d == 1) or (n == d) or (n == 6*d) : # conditions terminales
return 1
else : # sinon appels récursifs
if n > 6*(d-1) : # optimisation importante
debut = n - 6*(d-1)

for i in range(debut, 7) :
if n == i :
break
resultat += calcul(d-1, n-i)
return resultat

# programme principal =========================================================


d = int(input("Nombre de dés [2 .. { :d}] : ".format(MAX)))
while not(d >= 2and d <= MAX) :
d = int(input("Nombre de dés [2 .. { :d}], s.v.p. : ".format(MAX)))

n = int(input("Entrez un entier [{ :d} .. { :d}] : ".format(d, 6*d)))


while not(n >= d and n <= 6*d) :
n = int(input("Entrez un entier [{ :d} .. { :d}], s.v.p. : "
.format(d, 6*d)))

print("Il y a { :d} façon(s) de faire { :d} avec { :d} dés."


.format(calcul(d, n), n, d))

# -*- coding : utf8 -*-


# fichier : parfait_chanceux_m.py
# auteur : Bob Cordeau
112 Exercices corrigés

# définition de fonction
def somDiv(n) :
"""Retourne la somme des diviseurs propres de <n>."""
som_div = 1
for div in range(2, (n//2)+1) :
if n % div == 0:
som_div += div
return som_div

def estParfait(n) :
"""Retourne True si <n> est parfait, False sinon."""
return somDiv(n) == n

def estPremier(n) :
"""Retourne True si <n> est premier, False sinon."""
return somDiv(n) == 1

def estChanceux(n) :
"""Retourne True si <n> est chanceux, False sinon."""
est_chanceux = True
for i in range(0, n-1) :
est_chanceux = est_chanceux and estPremier(n + i + i*i)
return est_chanceux

# Auto-test ===================================================================
if name ==' main ' :
from verif_m import verif

verif(somDiv(12), 16)
verif(estParfait(6), True)
verif(estPremier(31), True)
verif(estChanceux(11), True)

# -*- coding : utf8 -*-


# fichier : parfait_chanceux.py
# auteur : Bob Cordeau

# import
from parfait_chanceux_m import estParfait, estChanceux

# programme principal =========================================================


parfait, chanceux = [], []

for n in range(2, 1001) :


if estParfait(n) :
[Link](n)
if estChanceux(n) :
[Link](n)

print("\nIl y a {} nombres parfaits dans [2, 1000] : {}"


.format(len(parfait), parfait))
print("\nIl y a {} nombres chanceux dans [2, 1000] : {}"
.format(len(chanceux), chanceux))


ANNEXE F

Glossaire

Lexique bilingue

>>
> Invite Python par défaut dans un shell interactif. Souvent utilisée dans les exemples de
code extraits de sessions de l’interpréteur Python.

… Invite Python par défaut dans un shell interactif, utilisée lorsqu’il faut entrer le code
d’un bloc indenté ou à l’intérieur d’une paire de parenthèses, crochets ou accolades.

2to3 Un outil qui essaye de convertir le code Python 2.x en code Python 3.x en gérant la
plupart des incompatibilités qu’il peut détecter.
2to3 est disponible dans la bibliothèque standard lib2to2; un point d’entrée autonome est
Tool/s-cipts/2to3. Voir 2to3 – Automated Python 2 to 3 code translation.

abstract base class ABC (classe de base abstraite)


Complète le duck-typing en fournissant un moyen de définir des interfaces alors que
d’autres tech- niques (comme hasattr()) sont plus lourdes. Python fournit de base plusieurs
ABC pour les struc- tures de données (module collections), les nombres (module numbers) et les
flux (module io). Vous pouvez créer votre propre ABC en utilisant le module abc.
argument (argument)
Valeur passée à une fonction ou une méthode, affectée à une variable nommée locale
au corps de la fonction. Une fonction ou une méthode peut avoir à la fois des arguments
par position et avec des valeurs par défaut. Ces arguments peuvent être de multiplicité
variable : * accepte ou fournit plusieurs arguments par position dans une liste, tandis que **
joue le même rôle en utilisant les valeurs par défaut dans un dictionnaire.
On peut passer toute expression dans la liste d’arguments, et la valeur évaluée est
transmise à la variable locale.
attribute (attribut)
Valeur associée à un objet référencé par un nom et une expression pointée. Par exemple,
l’attribut
a d’un objet o peut être référencé o.a.

BDFL Benevolent Dictator For Life (Dictateur Bienveillant à Vie)


Surnom bienveillant de Guido van R, le créateur de Python.
bytecode (bytecode ou langage intermédiaire)
Le code source Python est compilé en bytecode, représentation interne d’un programme
Python dans l’interpréteur. Le bytecode est également rangé dans des fichiers .pycet .pyo,
ainsi l’exécution d’un même fichier est plus rapide les fois ultérieures (la compilation du
source en bytecode peut être évitée). On dit que le bytecode tourne sur une ma ine
virtuelle qui, essentiellement, se réduit à une collection d’appels des routines correspondant
à chaque code du bytecode.
class (classe)
Modèle permettant de créer ses propres objets. Les définitions de classes contiennent
normalement des définitions de méthodes qui opèrent sur les instances de classes.
coercion (coercition ou transtypage)
Conversion implicite d’une instance d’un type dans un autre type dans une opération
concernant deux arguments de types compatibles. Par exemple, int(3.15) convertit le nombre
flottant 3.15 en
114 Glossaire

l’entier 3, mais dans 3+4.5, chaque argument est d’un type différent (l’un int et l’autre float) et
tous deux doivent être convertis dans le même type avant d’être additionnés, sinon une
exception TypeError sera lancée. Sans coercition, tous les arguments, même de types
compatibles, doivent être normalisés à la même valeur par le programmeur, par exemple,
float(3)+4.5au lieu de simplement
3+4.5.
complex number (nombre complexe)
Une extension du système familier des nombres réels dans laquelle tous les nombres sont
exprimés comme la somme d’une partie réelle et une partie imaginaire. Les nombres
imaginaires sont des multiples réels de l’unité imaginaire (la racine carrée de -1), souvent
écrite i par les mathématiciens et j par les ingénieurs. Python a un traitement incorporé des
nombres complexes, qui sont écrits avec cette deuxième notation ; la partie imaginaire est
écrite avec un suffixe j, par exemple 3+1j. Pour avoir accès aux équivalents complexes des
éléments du module math utilisez le module cmath. L’utilisation des nombres complexes est
une possibilité mathématique assez avancée. Si vous n’êtes pas certain d’en avoir besoin
vous pouvez les ignorer sans risque.
context manager (gestionnaire de contexte)
Objet qui contrôle l’environnement indiqué par l’instruction with et qui définit les méthodes
en-ter ()et exit (). Voir la PEP 343.
CPython (Python classique)
Implémentation canonique du langage de programmation Python. Le terme CPython est
utilisé dans les cas où il est nécessaire de distinguer cette implémentation d’autres comme
Jython ou IronPython.
decorator (décorateur)
Fonction retournant une autre fonction habituellement appliquée comme une
transformation util- isant la syntaxe @wrapper.
classmethod() et staticmethod() sont des exemples classiques de décorateurs.
Les deux définitions de fonctions suivantes sont sémantiquement
def f(...) :
...
f = staticmethod(f)

@staticmethod
def f(...) :
...

équivalentes :

Un concept identique existe pour les classes mais est moins utilisé. Voir la documentation
function definition et class definition pour plus de détails sur les décorateurs.
descriptor (descripteur)
Tout objet qui définit les méthodes get (), set () ou delete (). Lorsqu’un attribut d’une classe
est un descripteur, un comportement spécifique est déclenché lors de la consultation de
l’at- tribut. Normalement, écrire a.b consulte l’objet b dans le dictionnaire de la classe de a,
mais si best un descripteur, la méthode get () est appelée. Comprendre les descripteurs est
fondamental pour la compréhension profonde de Python, car ils sont à la base de
nombreuses caractéristiques, comme les fonctions, les méthodes, les propriétés, les
méthodes de classe, les méthodes statiques et les références aux super-classes.
Pour plus d’informations sur les méthodes des descripteurs, voir Implementing Descriptors.
dictionary (dictionnaire)
Une table associative, dans laquelle des clés arbitraires sont associées à des valeurs.
L’utilisation des objets dict ressemble beaucoup à celle des objets list, mais les clés peuvent
être n’importe quels objets ayant une fonction hash (), non seulement des entiers. Ces tables
sont appelées hash en Perl.
docstring (chaîne de documentation)
Chaîne littérale apparaissant comme première expression d’une classe, d’une fonction ou
d’un mod- ule. Bien qu’ignorée à l’exécution, elle est reconnue par le compilateur et
incluse dans l’attribut
doc de la classe, de la fonction ou du module qui la contient. Depuis qu’elle est disponible
via l’introspection, c’est l’endroit canonique pour documenter un objet.
du -typing (typage « comme un canard »)
Style de programmation pythonique dans lequel on détermine le type d’un objet par
Glossaire 115
inspection de ses méthodes et attributs plutôt que par des relations explicites à des types
(« s’il ressemble à un
116 Glossaire

canard et fait coin-coin comme un canard alors ce doit être un canard »). En mettant l’accent
sur des interfaces plutôt que sur des types spécifiques on améliore la flexibilité du code
en permettant la substitution polymorphe. Le duck-typing évite les tests qui utilisent type() ou
isinstance() (notez cependant que le duck-typing doit être complété par l’emploi des classes
de base abstraites). À la place, il emploie des tests comme hasattr() et le style de
programmation EAFP.
EAFP (Easier to ask for forgiveness than permission, ou « plus facile de demander pardon que
la permis- sion »)
Ce style courant de programmation en Python consiste à supposer l’existence des clés
et des at- tributs nécessaires à l’exécution d’un code et à attraper les exceptions qui se
produisent lorsque de telles hypothèses se révèlent fausses. C’est un style propre et rapide,
caractérisé par la présence de nombreuses instructions try et except. Cette technique
contraste avec le style LBYL, courant dans d’autres langages comme le C.
expression (expression)
Fragment de syntaxe qui peut être évalué. Autrement dit, une expression est une
accumulation d’éléments d’expression comme des littéraux, des noms, des accès aux
attributs, des opérateurs ou des appels à des fonctions retournant une valeur. À l’inverse
de beaucoup d’autres langages, toutes les constructions de Python ne sont pas des
expressions. Les instructions ne peuvent pas être util- isées comme des expressions (par
exemple if). Les affectations sont aussi des instructions, pas des expressions.
extension module (module d’extension)
Module écrit en C ou en C++, utilisant l’API C de Python, qui interagit avec le cœur du
langage et avec le code de l’utilisateur.
finder
Objet qui essaye de trouver le loader (chargeur) d’un module. Il doit implémenter une
méthode nommée find_module(). Voir la PEP 302 pour des détails et [Link] pour
une classe de base abstraite.
floor division (division entière)
Division mathématique qui laisse tomber le reste. L’opérateur de division entière est //. Par
exemple, l’expression 11//4est évaluée à 2, par opposition à la division flottante qui retourne
2.75.

function (fonction)
Suite d’instructions qui retourne une valeur à l’appelant. On peut lui passer zéro ou plusieurs
argu- ments qui peuvent être utilisés dans le corps de la fonction. Voir aussi argument et
method.
future
Un pseudo-module que les programmeurs peuvent utiliser pour permettre les nouvelles
fonction- nalités du langage qui ne sont pas compatibles avec l’interpréteur couramment
employé.
En important future et en évaluant ses variables, vous pouvez voir à quel moment une
carac- téristique nouvelle a été ajoutée au langage et quand est-elle devenue la
>>> import future
>>> future .division
_Feature((2, 2, 0, 'alpha', 2), (3, 0, 0, 'alpha', 0), 8192)

fonctionnalité par défaut :


garbage collection (gestion automatique de la mémoire)
Processus de libération de la mémoire quand elle n’est plus utilisée. Python exécute cette
gestion en comptant les références et en détectant et en cassant les références cycliques.
generator (fonction générateur)
Une fonction qui renvoie un itérateur. Elle ressemble à une fonction normale, excepté que
la valeur de la fonction est rendue à l’appelant en utilisant une instruction yield au lieu d’une
instruction return. Les fonctions générateurs contiennent souvent une ou plusieurs boucles
for ou while qui
« cèdent » des éléments à l’appelant. L’exécution de la fonction est stoppée au niveau du
mot-clé yield, en renvoyant un résultat, et elle est reprise lorsque l’élément suivant est requis
par un appel de la méthode next()de l’itérateur.
generator expression (expression générateur)
Une expression qui renvoie un générateur. Elle ressemble à une expression normale suivie
Glossaire 117
d’une expression for définissant une variable de contrôle, un intervalle et une expression if
facultative. Toute cette expression combinée produit des valeurs pour une fonction
englobante :
118 Glossaire

>>> sum(i*i for i in range(10)) # somme des �carrs 0, 1, 4, ... 81


285

GIL
Cf. global interpreter lo .
global interpreter lo (verrou global de l’interpréteur)
Le verrou utilisé par les threads Python pour assurer qu’un seul thread tourne dans la
ma ine virtuelle CPython à un instant donné. Il simplifie Python en garantissant que deux
processus ne peuvent pas accéder en même temps à une même mémoire. Bloquer
l’interpréteur tout entier lui permet d’être multi-thread aux frais du parallélisme du système
environnant. Des efforts ont été faits par le passé pour créer un interpréteur free-threaded
(où les données partagées sont verrouillées avec une granularité fine), mais les
performances des programmes en souffraient considérablement, y compris dans le cas des
programmes mono-thread.
hashable (hachable)
Un objet est hachable s’il a une valeur de hachage constante au cours de sa vie (il a besoin
d’une méthode hash ()) et s’il peut être comparé à d’autres objets (il a besoin d’une méthode
eq ()). Les objets hachables comparés égaux doivent avoir la même valeur de hachage.
L’hachabilité rend un objet propre à être utilisé en tant que clé d’un dictionnaire ou membre
d’un ensemble (set), car ces structures de données utilisent la valeur de hachage de façon
interne.
Tous les objets de base Python non modifiables (immutable) sont hachables, alors que
certains conteneurs comme les listes ou les dictionnaires sont modifiables. Les objets
instances des classes définies par l’utilisateur sont hachables par défaut ; ils sont tous
inégaux (différents) et leur valeur de hachage est leur id().
IDLE
Un environnement de développement intégré pour Python. IDLE est un éditeur basique et
un en- vironnement d’interprétation ; il est donné avec la distribution standard de Python.
Excellent pour les débutants, il peut aussi servir d’exemple pas trop sophistiqué pour tous
ceux qui doivent implé- menter une application avec interface utilisateur graphique multi-
plate-forme.
immutable (immuable)
Un objet avec une valeur fixe. Par exemple, les nombres, les chaînes, les tuples. De tels
objets ne peuvent pas être altérés ; pour changer de valeur un nouvel objet doit être créé.
Les objets immuables jouent un rôle important aux endroits où une valeurs de hash
constantes est requise, par exemple pour les clés des dictionnaires.
importer
Objet qui à la fois trouve et charge un module. C’est à la fois un objet finder et un objet
loader.
interactive (interactif)
Python possède un interpréteur interactif, ce qui signifie que vous pouvez essayer vos idées
et voir immédiatement les résultats. Il suffit de lancer python sans argument (éventuellement
en le sélec- tionnant dans un certain menu principal de votre ordinateur). C’est vraiment
un moyen puissant pour tester les idées nouvelles ou pour inspecter les modules et les
paquetages (pensez à help(x)).
interpreted (interprété)
Python est un langage interprété, par opposition aux langages compilés, bien que cette
distinction puisse être floue à cause de la présence du compilateur de bytecode. Cela
signifie que les fichiers source peuvent être directement exécutés sans avoir besoin de
créer préalablement un fichier binaire exécuté ensuite. Typiquement, les langages
interprétés ont un cycle de développement et de miseau point plus court que les langages
compilés mais leurs programmes s’exécutent plus lentement. Voir aussi interactive.
iterable
Un objet conteneur capable de renvoyer ses membres un par un. Des exemples
d’iterable sont les types séquences (comme les list, les str, et les tuple) et quelques types
qui ne sont pas desséquences, comme les objets dict, les objets file et les objets de n’importe
quelle classe que vous définissez avec une méthode iter () ou une méthode getitem (). Les
iterables peuvent être utilisés dans les boucles for et dans beaucoup d’autres endroits où
une séquence est requise (zip(), map(), . . .). Lorsqu’un objet iterable est passé comme
argument à la fonction incorporée iter() il renvoie un itérateur. Cet itérateur est un bon
moyen pour effectuer un parcours d’un ensemble de valeurs. Lorsqu’on utilise des
Glossaire 119

iterables, il n’est généralement pas nécesaire d’appeler la fonction


120 Glossaire

iter() ni de manipuler directement les valeurs en question. L’instruction for fait cela
automatique- ment pour vous, en créant une variable temporaire sans nom pour gérer
l’itérateur pendant la durée de l’itération. Voir aussi iterator, sequence, et generator.
iterator (itérateur)
Un objet représentant un flot de données. Des appels répétés à la méthode next () de
l’itéra- teur (ou à la fonction de base next()) renvoient des éléments successifs du flot.
Lorsqu’il n’y a plus de données disponibles dans le flot, une exception StopIteration est lancée.
À ce moment-là, l’ob- jet itérateur est épuisé et tout appel ultérieur de la méthode next() ne
fait que lancer encore une exception StopIteration. Les itérateurs doivent avoir une méthode
iter () qui renvoie l’objet itérateur lui-même. Ainsi un itérateur est itératif et peut être utilisé
dans beaucoup d’endroits où les iterables sont acceptés ; une exception notable est un
code qui tenterait des itérations multiples. Un objet conteneur (comme un objet list) produit
un nouvel itérateur à chaque fois qu’il est passé à la fonction iter()ou bien utilisé dans une
boucle for. Si on fait cela avec un itérateur on ne récupérera que le même itérateur épuisé
utilisé dans le parcours précédent, ce qui fera apparaître le conteneur comme s’il était vide.
keyword argument (argument avec valeur par défaut)
Argument précédé par variable_name= dans l’appel. Le nom de la variable désigne le nom local
dans la fonction auquel la valeur est affectée. ** est utilisé pour accepter ou passer un
dictionnaire d’arguments avec ses valeurs. Voir argument.
lambda
Fonction anonyme en ligne ne comprenant qu’une unique expression évaluée à l’appel.
Syntaxe de création d’une fonction lambda :
lambda[arguments]: expression

LBYL (Look before you leap ou « regarder avant d’y aller »)


Ce style de code teste explicitement les pré-conditions avant d’effectuer un appel ou une
recherche. Ce style s’oppose à l’approche EAFP et est caractérisé par la présence de
nombreuses instructions
if.
list (liste)
Séquence Python de base. En dépit de son nom, elle ressemble plus au tableau
d’autres langages qu’à une liste chaînée puisque l’accès à ses éléments est en O(1).
list comprehension (liste en compréhension)
Une manière compacte d’effectuer un traitement sur un sous-ensemble d’éléments d’une
séquence en renvoyant une liste avec les résultats. Par exemple :
result = ["0x%02x" % x for x in range(256) if x % 2 == 0]

engendre une liste de chaînes contenant les écritures hexadécimales des nombres pairs de
l’intervalle de 0 à 255. La clause if est facultative. Si elle est omise, tous les éléments de
l’intervalle range(256)seront traités.
loader (chargeur)
Objet qui charge un module. Il doit posséder une méthode load_module(). un loader est
typiquement fournit par un finder. Voir la PEP 302 pour les détails et voir
[Link] pour une classe de base abstraite.
mapping (liste associative)
Un objet conteneur (comme dict) qui supporte les recherches par des clés arbitraires en
utilisant la méthode spéciale getitem ().
metaclass
La classe d’une classe. La définition d’une classe crée un nom de classe, un dictionnaire et
une liste de classes de base. La métaclasse est responsable de la création de la classe à
partir de ces trois éléments. Beaucoup de langages de programmation orientés objets
fournissent une implémentation par défaut. Une originalité de Python est qu’il est possible
de créer des métaclasses personnalisées. Beaucoup d’utilisateurs n’auront jamais besoin
de cela mais, lorsque le besoin apparaît, les méta- classes fournissent des solutions
puissantes et élégantes. Elles sont utilisées pour enregistrer les accès aux attributs, pour
ajouter des threads sécurisés, pour détecter la création d’objet, pour implé- menter des
singletons et pour bien d’autres tâches.
Plus d’informations peuvent être trouvées dans Customizing class creation.
Glossaire 121

method (méthode)
Fonction définie dans le corps d’une classe. Appelée comme un attribut d’une instance de
classe, la méthode prend l’instance d’objet en tant que premier argument (habituellement
nommé self). Voir function et nested scope.
mutable (modifiable)
Les objets modifiables peuvent changer leur valeur tout en conservant leur id(). Voir
aussi im- mutable.
named tuple (tuple nommé)
Toute classe de pseudo-tuples dont les éléments indexables sont également accessibles
par des at- tributs nommés (par exemple [Link]() retourne un objet pseudo-tuple où
l’année est ac- cessible soit par un index comme t[0]soit par un attribut nommé comme
t.tm_year.
Un tuple nommé peut être un type de base comme time.struct_time ou il peut être créé par une
définition de classe ordinaire. Un tuple nommé peut aussi être créé par la fonction fabrique
[Link](). Cette dernière approche fournit automatiquement des caractéristiques
supplé- mentaires comme une représentation auto-documentée, par exemple :
>>> Employee(name='jones', title='programmer')

namespace (espace de noms)


L’endroit où une variable est conservée. Les espaces de noms sont implémentés comme
des diction- naires. Il y a des espace de noms locaux, globaux et intégrés et également
imbriqués dans les objets. Les espaces de noms contribuent à la modularité en prévenant
les conflits de noms. Par exemple, les fonctions builtin .open() et [Link]() se distinguent par
leurs espaces de noms. Les espaces de noms contribuent aussi à la lisibilité et la
maintenablité en clarifiant quel module implémente une fonction. Par exemple, en écrivant
[Link]() ou [Link]() on rend évident que ces fonctions sont implémentées dans les
modules randomet itertools respectivement.
nested scope (portée imbriquée)
La possibilité de faire référence à une variable d’une définition englobante. Par exemple, une
fonction définie à l’intérieur d’une autre fonction peut faire référence à une variable de la
fonction extérieure. Notez que le portées imbriquées fonctionnent uniquement pour la
référence aux variables et non pour leur affectation, qui concerne toujours la portée
imbriquée. Les variables locales sont lues et écrites dans la portée la plus intérieure ; les
variables globales sont lues et écrites dans l’espace de noms global. L’instruction nonlocal
permet d’écrire dans la portée globale.
new-style class (style de classe nouveau)
Vieille dénomination pour le style de programmation de classe actuellement utilisé. Dans
les ver- sions précédentes de Python, seul le style de classe nouveau pouvait bénéficier
des nouvelles car- actéristiques de Python, comme slots , les descripteurs, les propriétés,
getattribute (), les méthodes de classe et les méthodes statiques.
object (objet)
Toute donnée comprenant un état (attribut ou valeur) et un comportement défini
(méthodes). Égale- ment la classe de base ultime du new-style class.
positional argument (argument de position)
Arguments affectés aux noms locaux internes à une fonction ou à une méthode,
déterminés par l’ordre donné dans l’appel. La syntaxe * accepte plusieurs arguments de
position ou fournit une liste de plusieurs arguments à une fonction. Voir argument.
property (propriété)
Attribut d’instance permettant d’implémenter les principes de l’encapsulation.
Python3000
Surnom de la version 3 de Python (forgé il y a longtemps, quand la version 3 était un projet
lointain). Aussi abrégé « Py3k ».
Pythonic (pythonique)
Idée ou fragment de code plus proche des idiomes du langage Python que des concepts
fréquemment utilisés dans d’autres langages. par exemple, un idiome fréquent en Python
est de boucler sur les éléments d’un iterable en utilisant l’instruction for. Beaucoup d’autres
langages n’ont pas ce type de construction et donc les utilisateurs non familiers avec Python
utilisent parfois un compteur numérique :
122 Glossaire

for i in range(len(food)) :
print(food[i])

Au lieu d’utiliser la méthode claire et pythonique :


for piece in food :
print(piece)

reference count (nombre de références)


Nombre de références d’un objet. and le nombre de références d’un objet tombe à zéro,
l’objet est désalloué. Le comptage de références n’est généralement pas visible dans le code
Python, mais c’est un élément clé de l’implémentation de CPython. Le module sys définit la
fonction getrefcount() que les programmeurs peuvent appeler pour récupérer le nombre de
références d’un objet donné.
slots
Une déclaration à l’intérieur d’une classe de style nouveau qui économise la mémoire en
pré- déclarant l’espace pour les attributs et en éliminant en conséquence les dictionnaires
d’instance. Bien que populaire, cette technique est quelque peu difficile à mettre en place
et doit être réservée aux rares cas où il y a un nombre important d’instances dans une
application où la mémoire est réduite.
sequence (séquence)
Un iterable qui offre un accès efficace aux éléments en utilisant des index entiers et les
méthodes spé- ciales getitem ()et len (). Des types séquences incorporés sont list, str, tupleet
unicode. Notez que le type dictcomporte aussi les méthodes getitem ()et len (), mais est considéré
comme une table associative plutôt que comme une séquence car la recherche se fait à
l’aide de clés arbitraires immuables au lieu d’index.
slice (tranche)
Objet contenant normalement une partie d’une séquence. Une tranche est créée par
une nota- tion indexée utilisant des « : » entre les index quand plusieurs sont donnés,
comme dans vari- able_name[1:3:5]. La notation crochet utilise les objets slice de façon interne.
special method (méthode spéciale)
Méthode appelée implicitement par Python pour exécuter une certaine opération sur un
type, par exemple une addition. Ces méthodes ont des noms commençant et finissant par
deux caractères soulignés. Les méthodes spéciales sont documentées dans Special
method names.
statement (instruction)
Une instruction est une partie d’une suite, d’un « bloc » de code. Une instruction est soit
une ex- pression soit une ou plusieurs constructions utilisant un mot clé comme if, while ou
for.
triple-quoted string (chaîne multi-ligne)
Une chaîne délimitée par trois guillemets (”) ou trois apostrophes (’). Bien qu’elles ne
fournissent pas de fonctionnalités différentes de celles des chaînes simplement délimitées,
elles sont utiles pour nombre de raisons. Elles permettent d’inclure des guillemets ou des
apostrophes non protégés et elles peuvent s’étendre sur plusieurs lignes sans utiliser de
caractère de continuation, et sont donc spécialement utiles pour rédiger des chaînes de
documentation.
type (type)
Le type d’un objet Python détermine de quelle sorte d’objet il s’agit ; chaque objet possède
un type. Le type d’un objet est accessible grâce à son attribut class ou peut être retourné par
la fonction
type(obj).

view (vue)
Les objets retournés par [Link](), [Link]() et [Link]() sont appelés des dictionary views.
ce sont des « séquences paresseuses 1 » qui laisseront voir les modifications du dictionnaire
sous-jacent. Pour forcer le dictionary view à être une liste complète, utiliser list(dictview). Voir
Dictionary view objects.
virtual ma ine (machine virtuelle)
Ordinateur entièrement défini par un programme. la machine virtuelle Python exécute le
bytecode généré par le compilateur.
1. L’évaluation paresseuse est une technique de programmation où le programme n’exécute pas de code avant que
les résultats de ce code ne soient réellement nécessaires. Le terme paresseux (en anglais lazzy evaluation) étant connoté
négativement en français on parle aussi d’évaluation retardée.
Glossaire 123

Zen of Python
Une liste de principes méthodologiques et philosophiques utiles pour la compréhension et
l’utili- sation du langage Python. Cette liste peut être obtenue en tapant import this dans
l’interpréteur Python.
124 Glossaire

Webographie
• Les sites généraux
: [Link]
[Link]/py
pi
[Link]/p/pythonxy/wiki/Downloads
• Interpréteur et EDI
spécialisés : IP[y]thon
Spyder
Wing IDE 101
SciTE
PyScripter
• Les outils :
matplotlib
inkscape
rst2pdf
TeXnicCent
er
• Le lien des
liens : Liens
Python
Bibliographie

[1] S , Gérard, Apprendre à programmer avec Python 3, Eyrolles, 2010.


[2] S , Mark, Programming in Python 3, Addison-Wesley, 2e édition, 2009.
[3] M , Alex, Python en concentré, O’Reilly, 2004.
[4] M , Alex, M R , Anna, A , David, Python par l’exemple, O’Reilly, 2006.
[5] L , Mark et B , Yves, Python précis et concis, O’Reilly, 2e édition, 2005.
[6] Z , Tarek, Programmation Python. Conception et optimisation, Eyrolles, 2e édition, 2009.
[7] Z , Tarek, Python : Petit guide à l’usage du développeur agile, Dunod, 2007.
[8] H , Doug, e Python Standard Library by Example, Addison-Wesley, 2011.
[9] L , Hans Petter, A Primer on Scientific Programming with Python, Springer, 2011.
[10] B , David M., Python. Essential Reference, Addison Wesley, 4e édition, 2009.
[11] Y , Jeff, Foundations of Agile Python Development, Apress, 2008.
[12] R Maïeul, LATEX appliqué aux sciences humaines, Atramenta, 2012.
[13] C Céline et collectif, LATEX pour l’impatient, H & K, 3e édition, 2009.
[14] C , David, Règles typographiques et normes. Mise en pratique avec LATEX, Vuibert, 2006.
Mémento Bases Python 3

entier, flottant, booléen, chaîne Types de base

10-6 non modifiable

' échappé
couples clé/vale ur
non modifiable,
pour nomsordonnée
séquence de variables,
de caractères
Identificateurs Conversions
fonctions, modules, classes…
a‥zA‥Z_ suivi de a‥zA‥Z_0‥9

☺ voir au verso le formatage de chaînes, qui permet un contrôle fin


☹ 8y and

Affectation de variables
la séquence en paramètre

utilise chaque élément de


la séquence en paramètre

variables
incrémentation

pour les listes, tuples, chaînes de caractères,… Indexation des séquences

index négatif -6 -5 -4 -3 -2 -1
index positif 0 1 2 3 4 5
tranche positive
tranche négative

Indication de tranche manquante → à partir du début / jusqu'à la fin.


Su les séquenc ifiable utilisable pou pp ession et dification pa affectation

Logique booléenne Blocs d'instructions bloc d'instructions exécuté Instruction conditionnelle


Comparateurs: < > <= >= == != instruction parente: uniquement si une condition est vraie
≤ ≥ = ≠
bloc d'instructions 1…
a and b et logique
indentation !

les deux en même temps bloc d'instructions


b instruction parente:
l'un ou l'autre ou les deux
bloc d'instructions 2… exemple :
True
valeur constante faux instruction suivante après bloc 1 # bloc si expression logique x==42 vraie

☝ nombres flottants… valeurs approchées ! angles en radians Maths # bloc sinon si expression logique x>0 vraie
%
# bloc sinon si variable booléenne bTermine vraie

# bloc sinon des autres cas restants


etc. (cf doc
bloc d'instructions exécuté Instruction boucle itérative
bloc d'instructions exécuté Instruction boucle
pour
conditionnelle

tant que la condition est vraie chaque élément d'un conteneur ou d'un itérateur
while expression for variable in séquence:
logique:

s = bloc Contrôle de boucle bloc d'instructions


0 d'instructions break
sortie Parcours des valeurs de la séquence
i = initialisations avant la boucle
1 immédiate

condition avec au moins une valeur variable continue s = "Du initialisations avant la boucle
(ici i) itération
suivante
texte" cpt = 0
while i <= 100:
variable de boucle, valeur gérée par l'instruction for
# bloc exécuté tant que i ≤ i=100 for c in s:
100

s= ∑ i2
s = s + i**2 if c == "e": Comptage du
i = i + 1 ☝ faire varier la cpt = cpt + nombre de edans la
variable 1 chaîne.

de condition ! i=1 print("trouvé",cpt,"'e'")


print("somme:", résultat de calcul après la boucle sur dict/set = boucle sur séquence des clés
s) boucle

☝ attention aux boucles sans fin ! utilisation des tranches pour parcourir un sous-ensemble de la séquence
Parcours des index de la séquence
Affichage / Saisie ◽ changement de l'élément à la position
print("v=",3,"cm :",x,",",y+4) ◽ accès aux éléments autour de la position (avant/après)
lst = [11,18,9,12,23,4,17]
éléments à afficher : valeurs littérales, variables, perdu = []
for idx in Bornage des
expressions Options de print: valeurs
◽ sep=" " (séparateur d'éléments, défaut espace) range(len(lst)): val =
supérieures à 15,
◽ end="\n" (fin d'affichage, défaut fin de ligne) lst[idx]
mémorisation des
if val> 15:
◽ file=f (print vers fichier, défaut sortie standard) valeurs perdues.
[Link](va
l) lst[idx] = 15

s = input("Directives:") print("modif:",lst,"-modif:",perdu)
☝ input retourne toujours une chaîne, la convertir vers le type Parcours simultané index et valeur de la séquence:
désiré (cf encadré Conversions au recto). for idx,val in enumerate(lst):

nb d'éléments Opérations sur conteneurs très utilisé pour les Génération de séquences d'entiers
Note: Pour dictionnaires et ensembles, boucles itératives for
ces opérations travaillent sur les clés.
→ booléen, opérateur de test de présence d'absence)

Spécifique aux conteneurs de séquences (listes, tuples, chaînes) :


Définition de fonction

Opérations sur dictionnaires Opérations sur ensembles # bloc instructions, calcul de res, etc.
clé valeu Opérateurs:

retourner None
i e à jou /ajout
de couple < <= > >= → relations d'inclusion
vue u le clé , clé pp l d fon ion
valeu , couple clé
clé clé

stockage de données sur disque, et relecture Fichiers


Formatage de chaînes
directives de formatage valeurs à formater
variable nom du fichier
◽ lecture (read) "modele{} {} str
◽ écriture (write) {}".format(x,y,r)
◽ "{sélection:formatage!conversion}
cf fonctions des modules os et [Link] … "
en écriture
◽ Sélection : "{:+2.3f}".format(45.7273)
2 →'+45.727'
☝ fichier texte → lecture / écriture x Exemples "{1:>10s}".format(8,"toto")
de chaînes uniquement, convertir suivante [Link]
4[clé
→' toto'
de/vers le type désiré "{!r}".format("L'ame")
☝ ne pas oublier de refermer le fichier après son utilisation ! ]
0[2] →'"L\'ame"'
Opérations sur listes ◽ Formatage
:
bloc de traitement de la ligne car-rempl. alignement signe [Link]écision~[Link] type
< > ^ = + - espace 0 au début pour remplissage avec des 0
entiers: b binaire, c caractère, d décimal (défaut), o octal, x ou X hexa…
flottant: e ou E exponentielle, f ou F point fixe, g ou G approprié (défaut),
% pourcentage
chaîne : s …
◽ Conversion : s (texte lisible) ou r (représentation littérale)

Vous aimerez peut-être aussi