Ressource de formation Python par l’exemple
...
3.2 Structures de base
3.2.1 Commentaires
Comme dans la majorité des langages de script, les commentaires Python sont définis à l’aide du caractère #. Qu’il soit utilisé comme premier caractère ou non, le # introduit un commen-taire jusqu’à la fin de la ligne. Comme toujours, les commentaires sont à utiliser abondamment avec parcimonie. Il ne faut pas hésiter à commenter le code, sans pour autant mettre des com-mentaires qui n’apportent rien. Le listing suivant présente une ligne de commentaire en Python. Les commentaires seront de nouveaux abordés en [sub :chap1 :doc] pour l’auto-documentation. Les commentaires introduits par # devraient être réservés au remarques sur le code en sa mise en oeuvre.
# ceci est un commentaire
print ’bouh’ # ceci est aussi un commentaire
bouh
3.2.2 Typage en Python
En Python, tout est objet. Quelque soit les données que l’on manipule, ces données sont des ob-jets dont les classes sont définies par l’usager, ou par l’environnement Python pour les types de base. Une conséquence est que l’utilisateur manipule les données au travers de références (qui donnent accès aux fonctionnalités des instances). Cette approche permet de rendre homogène les données manipulées par l’utilisateur (comme c’était le cas en SmallTalk). Ainsi, toutes les données suivantes suivant sont des objets : 1, [2, 3, 4], 5.6, ‘toto’, une instance de Foo.
Python est un langage à typage dynamique. Ceci ne veut pas dire que les données que l’on manipule ne sont pas typées, mais que leur type est «calculé» lors de leur utilisation 2. Dans ce contexte, le type des variables n’est pas défini explicitement par l’utilisateur. Ainsi, une même variable peut référencer dans un programme des objets de types différents 3.
…
Foo() # x reference desormais une instance de Foo
En Python, ce calcul se résume à la possibilité pour l’objet de recevoir un message particulier.
Il est toutefois à noter que cette facilité ne devrait être utilisées que sous couvert du polymorphisme, sans quoi la lisibilité du programme s’en trouve réduite.
3.2.3 Arithmétique
Python permet d’exprimer très simplement des opérations arithmétiques. Dans le cas où tous les opérandes sont des entiers, alors les résultats seront aussi des entiers. Lorsqu’au moins un des opérandes est de type réel, alors tous les opérandes sont automatiquement convertis en réels.
…
Dans le contexte de l’arithmétique, les affections peuvent prendre deux formes. Ces deux formes ont un sens différent. Le listing suivant présente deux affectations qui pourraient être comprises de manière identique. Toutefois, la première forme (ligne 2) a pour conséquence de créer une nouvelle instance d’entier pour contenir l’ajout de 2 à la valeur de x. La seconde forme (ligne 3) ajoute 2 à la valeur de x sans créer de nouvelle instance. La manière d’écrire une opération a donc un impact sur son évaluation.
x = 4
x = x + 2
x += 2
3.2.4 Chaînes de caractères
Les chaînes de caractères se définissent de plusieurs manières en Python. Il est possible d’uti-liser indifféremment des guillemets simples ou des guillemets doubles. Le choix est souvent imposé par le contenu de la chaîne : une chaîne contenant des guillemets simples sera décla-rée avec des guillemets doubles et réciproquement. Pour les autres cas, c’est indifférent. Enfin, comme tout est objet en Python une chaîne est donc un objet. Le listing suivant déclarer deux chaînes référencées par x et y. Enfin, la chaîne référencée par z est une chaîne de caractères multi-lignes (utilisation de trois quotes / guillemets simples ou doubles).
x = ’hello ’
y = "world!"
z = ’’’hello
... world’’’
3.2.5 Concaténation
La concaténation de ces chaînes de caractères peut prendre deux formes. Dans les deux cas, l’opérateur + est utilisé pour exprimer la concaténation. La forme de la ligne 4 est un raccourci d’écriture.
z = x + y
z
’hello world!’
x += y
x
’hello world!’
3.2.6 Affichage
L’affichage de chaînes de caractères à l’aide de la fonction print peut se faire en concaténant explicitement des chaînes (que ce soit avec l’opérateur de concaténation ou en utilisant des virgules) ou en utilisant une chaîne de formatage comme la fonction printf du langage C. Cette seconde option est un peu plus puissante, mais aussi un peu plus lourde à utiliser. Le listing suivant présente trois manière d’afficher des chaîne de caractères.
print ’I say: ’ + x I say: hello world!
print x, 2, ’times’ hello world! 2 times
print "I say: %s %d time(s)" % (x, 2) I say: hello world! 2 time(s)
3.2.7 Manipulations
Le listing suivant donne quelques exemples d’accès à un caractère (ligne 2), ou à une sous-chaîne pour le reste. La colonne de gauche présente des accès à partir du début de la chaînes (les index sont positifs). La ligne 6 signifie que l’on souhaite le contenu de x du quatrième caractère à la fin. Enfin, la dernière ligne réalise une copie de la chaîne x.
….
x[3:]
’lo world!’
x[:] ’hello world!’
Enfin, un index négatifs précise que le calcul s’effectue depuis la fin de la chaîne.
x[-3:] ’ld!’
x[1:-1] ’ello world’
3.2.8 Listes
Les listes Python sont des ensemble ordonnés et dynamique d’éléments. Ces ensemble peuvent contenir des éléments de différents types, leur seul point commun est que ce sont des objets. Et comme tout est objet, les listes sont elles mêmes des objets (instances de la classe list).
L’exemple suivant crée tout d’abord deux listes vides avec les deux manières possibles.
mylist = []
mylist2 = list()
Ensuite, après avoir défini x, une liste contenant la chaîne ‘bar’, l’entier 12345 et l’objet réfé-rencé par la variable x est créée.
x = True
foo = [’bar’, 12345, x]
foo
[’bar’, 12345, True]
Les listes et les chaînes ont pour point commun le fait d’être des ensembles ordonnés. L’accès un élément d’une liste se fait donc aussi par indexation (ligne 1). Il est possible de prendre une partie de liste (ligne 3) et d’obtenir une copie de liste (ligne 5). Une copie de liste crée une nouvelle liste, mais partage les éléments contenus dans la liste d’origine.
foo[2]
True
foo[1:] [12345, True]
bar = foo[:]
Enfin, le contenu de la liste peut être changé par simple affection en utilisant l’index cible. Comme la liste référencée par bar est une copie de la liste référencée par foo, le dernier élément de bar n’est pas modifié par l’affectation du dernier élément de foo.
foo[2] = 1
foo
[’bar’, 12345, 1]
bar[-1]
True
L’ajout d’éléments dans une liste se fait à l’aide des méthodes append pour un ajout en fin de liste, et insert, pour un ajout à un index donné. Enfin, la méthode extend ajoute le contenu d’une liste passé en paramètre à la fin de la liste.
foo.append(’new’)
foo
[’bar’, 12345, 1, ’new’]
foo.insert(2, ’new’)
foo
[’bar’, 12345, ’new’, 1, ’new’]
foo.extend([67, 89])
foo
[’bar’, 12345, ’new’, 1, ’new’, 67, 89]
La méthode index permet de connaître l’index de la première occurrence d’un élément dans une liste. Dans le cas où l’élément fournit en paramètre n’est pas présent, la méthode lève l’exception ValueError. L’utilisation de la construction in retourne quant à elle True si l’élément est présent dans la liste et False sinon.
foo.index(’new’)
foo.index(34)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ValueError: list.index(x): x not in list
34 in foo False
…
[0, 1, 1, 0]
bar += [2, 3]
bar
[0, 1, 1, 0, 2, 3]
[0, 1] * 3
[0, 1, 0, 1, 0, 1]
Attention Dans le cas d’une création de liste par répétition, les éléments ne sont pas dupliqués, mais ce sont leurs références qui sont répétées (et donc présentes plusieurs fois dans la liste finale). Utiliser l’opérateur * pour la construction de matrice est donc une mauvaise idée car toutes les lignes seraient alors la même liste référencée plusieurs fois.
matrix = [[1, 2, 3]] * 3
matrix
[[1, 2, 3], [1, 2, 3], [1, 2, 3]]
matrix[1][1] = 4
matrix
[[1, 4, 3], [1, 4, 3], [1, 4, 3]]
De manière symétrique à l’ajout d’éléments dans une liste, il est possible d’en supprimer. La méthode remove permet de supprimer la première occurrence d’un élément d’une liste en le désignant.
foo.remove(’new’)
foo
[’bar’, 12345, 1, ’new’, 67, 89]
Si l’élément fourni en paramètre n’existe pas dans la liste, l’exception ValueError est levée.
foo.remove(34)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ValueError: list.remove(x): x not in list
L’opérateur del (delete) permet de détruire une référence à un objet Python, ou à une partie d’une liste 4.
del foo[1:3]
foo
[’bar’, ’new’, 67, 89]
D’autres formes plus avancées de manipulation de listes sont présentées dans la section [sub :chap1 :listes].
3.2.9 Listes et chaînes de caractères
Les listes et les chaînes de caractères sont similaire dans leur structure et dans leur manipula-tion. Certaines méthodes disponibles sur les chaînes manipulent les deux structures de données.
’ ; ’.join([’a’, ’b’, ’c’]) ’a ; b ; c’
Lorsque la dernière référence à un objet est détruite alors l’objet et lui même effectivement détruit.
De manière symétrique, la méthode split, disponible sur les chaînes, permet de décomposer une chaîne de caractères en une liste de sous chaînes. Ce découpage se fait par rapport à un ou plusieurs caractères. Dans le cas où un entier est passé comme second argument, il précise le nombre maximum de découpage.
’hello crazy world!’.split(" ") [’hello’, ’crazy’, ’world!’]
’hello crazy world!’.split(" ", 1) [’hello’, ’crazy world!’]
3.2.10 Tuples
Les tuples sont des ensemble ordonnés et immuables d’éléments. Comme les listes, les tuples peuvent contenir des données de différents types. La première ligne présente une décla-ration classique (avec des parenthèses) alors que la secone ligne présente la notation abrégée. La virgule est importante pour préciser que l’on parle d’un tuple à un élément et non de la valeur 12. Cette remarque serait valable dans la cas d’une déclaration parenthésée d’un tuple à un élément.
foo = (’bar’, 12345, x)
bar = 12,
Comme les listes, les tuples sont accessibles par indexation, et la construction in permet de tester la présence d’un élément dans le tuple. Cependant, une fois créé le contenu d’un tuple ne peut être modifié.
…
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: ’tuple’ object does not support item assignment
Remarque Critères de choix entre une liste et un tuple :
– les tuples sont plus rapides à parcourir que les listes,
– pour définir des constantes, utiliser des tuples.
Il est possible de convertir des tuples vers des listes et réciproquement.
foo = tuple([1, 2, 3])
foo
(1, 2, 3)
Pour terminer, Python offre l’affectation multiple pour assigner différentes valeurs depuis un tuple de manière simultanée. Ici encore, les parenthèses peuvent être omises. Il est aussi pos-sible de faire des assignation en cascade.
…
3.2.11 Dictionnaires
Les dictionnaires, parfois appelés tableaux associatifs, sont des ensembles non ordonnés d’élé-ments indexés par des clés. Une clé doit obligatoirement être immuable (chaîne, entier ou tuple). D’autre part, une clé est toujours unique.
Un dictionnaire est déclaré par deux accolades ou en utilisant le type dict. Il peut être créé vide ou avec des valeurs initiales.
dict1 = {}
dict2 = {’foo’: 456, 123: ’bar’}
dict3 = dict()
dict4 = dict(foo=456, bar=123)
L’ajout et l’accès aux éléments d’un dictionnaire se fait par indexation sur la clé associé à l’élément.
dict1[’foo’] = 456
dict1[123] = ’bar’
dict1[123]
’bar’
L’affichage d’un dictionnaire présente une liste de paires «clé : valeur».
dict1
{123: ’bar’, ’foo’: 456}
dict4
{’foo’: 456, ’bar’: 123}
L’utilisation de la classe dict pour créer un dictionnaire non vide, ne peut se faire qu’avec des chaines (utilisation d’arguments nommés).
dict4
{’foo’: 456, ’bar’: 123}
Les dictionnaires offrent des méthodes pour manipuler les clés. La méthode keys retourne une liste de toutes les clés du dictionnaire et la méthode has_key retourne True si la clé donné en paramètre est présente dans le dictionnaire et False dans le cas contraire. Il est aussi possible d’utiliser l’opérateur in
dict1.keys() [123, ’foo’]
123 in dict1
True
La méthode values donne accès à une liste des valeurs contenues dans le dictionnaire et la méthode items donne une liste de tuples, contenant chacun une paire clé, valeur.
dict1.values() [’bar’, 456]
dict1.items()
[(123, ’bar’), (’foo’, 456)]
La modification d’une valeur associée à une clé se fait simplement en affectant de nouveau la valeur indexée dans le dictionnaire par la clé en question. Enfin, l’opérateur del permet de supprimer une association du dictionnaire.
dict1[123] = 789
dict1
{123: 789, ’foo’: 456}
del dict1[’foo’]
dict1
{123: 789}
A l’aides des méthodes de manipulation des clé et des valeurs, il est possible de parcourir un dictionnaire de plusieurs manières. Les quelques lignes suivantes donnent deux exemples de parcours.
flames = {’windows’: ’bof’, ’unix’: ’cool’}
for key in flames.keys():
... print key, ’is’, flames[key]
...
windows is bof
unix is cool
for key, value in flames.items():
...print key, ’is’, value
...
windows is bof unix is cool
3.3 Constructions
3.3.1 Structuration et indentation
La structuration d’un programme Python est définie par son indentation. Le début d’un bloc est défini par un ‘:‘, la première ligne pouvant être considérée comme un en-tête (test, boucle, définition, etc.). Le corps du bloc est alors indenté de manière plus importante (mais régulière) que l’en-tête. Enfin, la fin du bloc est délimité par le retour à l’indentation de l’en-tête. La convention en Python est d’utiliser quatre espaces pour chaque niveau d’indentation. Les bloc peuvent être imbriqués.
<en-tete>:
Dans le cas de bloc de taille réduite, par exemple une seule instruction, un bloc peut être défini sur une seule ligne. Le caractère : sert toujours à délimiter l’en-tête du corps. Cependant, cette utilisation n’est pas vraiment bénéfique quant à la lisibilité du code, si ce n’est pour faire tenir du code sur un transparent. Elle est donc à éviter, l’époque où les caractères étaient comptés dans un fichier est bien révolue.
<en-tete>: <instruction>
Cette structuration est utilisée aussi bien pour définir des boucles, des tests, des fonctions, des classes ou encore des méthodes.
3.3.2 Tests
Conditions booléennes En Python, tout ce qui n’est pas faux est vrai. Les conditions boo-léennes fausses se résument au données «vide» en plus du faux :
False, None, 0, "", [], list(), {}, dict(), (), tuple()
Expression booléennes Python propose les opérateurs de comparaison et les opérateurs boo-léen suivants :
<, <=, >, >=, !=, ==, is
and, or, not
L’utilisation du == permet de comparer l’équivalence de valeurs (donc d’objets), alors que is permet de comparer si deux variables référence une même instance.
l1 = [1, 2, 3]
l2 = [1, 2, 3]
l1 == l2
True
l1 is l2 False
l3 = l1
l3 is l1
True
Python offre une seule construction pour réaliser des tests : le if then else. Une particu-larité de cette mise en oeuvre est la possibilité d’enchaîner les tests avec la construction elif. Le else et le elif sont bien sûr optionnels. Enfin, a clause elif peut être utilisée plusieurs fois dans un même bloc.
if x == ’hello’:
print ’hello too!’
elif x == ’bonjour’:
print ’bonjour aussi!’
else:
print ’moi pas comprendre’
oeufs = 4 if souhaite_une_tarte() else 0
L’absence du switch se justifie par le fait que Python est un langage objet. Dans cette ap-proche, l’utilisation du polymorphisme replace naturellement le switch. Lorsque l’on ne dé-veloppe pas avec une approche objet, le switch se remplace simplement par l’utilisation d’un dictionnaire. Ce dictionnaire contient, par exemple, des fonctions associées à des clés (pas né-cessairement des chaînes) pour choisir la bonne fonction en fonction du contexte
traitements = {
’en’: traiter_anglais,
’de’: traiter_allemand,
’fr’: traiter_francais,
# etc.
}
def traiter_commande(langue, commande): traiter = traitements[langue] traiter(commande)
Les comparaisons entre chaînes de caractères se fait selon l’ordre lexicographique. Les compa-raisons entre listes et tuples se fait éléments par éléments de la gauche vers la droite.
’abc’ < ’abd’
True
’abc’ < ’abcd’
True
(1, 2, 3) < (1, 2, 4)
True
(1, 2, 3) < (1, 2, 3, 4)
True
(1, 2, 3, 4) < (1, 2, 4)
True
3.3.3 Boucles
Deux types de boucles sont disponibles : les boucles énumérées (for) et les boucles basées sur un test de fin (while). Ces deux constructions suivent le même schéma : un en-tête qui décrit l’évolution de la boucle, un ensemble d’instructions qui sont évaluées à chaque tour de boucle et une partie optionnel qui est évaluée en sortie de boucle (introduite par le mot-clé else). Enfin, comme en C les boucles peuvent contenir les branchements continue pour passer à l’itération suivante et break pour sortir de la boucle (dans ce cas la clause else n’est pas évaluée).
Boucles énumérées
for <var> in <sequence>:
<instructions>
else:
<instructions, sequence epuisee sans break>
La fonction range produit une liste de tous les entiers entre une borne inférieur et une borne supérieur. Cette construction est utile lorsqu’une boucle for repose sur l’utilisation d’une sé-quence d’entiers. La fonction range est utilisable de trois manières :
– un seul paramètre spécifiant le nombre d’éléments (ligne 1),
– deux paramètres spécifiant la borne inférieure (inclue) et supérieure (exclue) (ligne 3),
– trois paramètres spécifiant les bornes et le saut (incrément entre deux éléments de la sé-quence) (ligne 5).
range(6)
[0, 1, 2, 3, 4, 5]
range(3, 7) [3, 4, 5, 6]
range(0, 10, 3) [0, 3, 6, 9]
Boucle for parcourant une séquence de chaîne.
a = [’hello’, ’world’]
for elt in a:
... print elt
...
hello
world
Boucle parcourant une liste de valeurs pour accéder aux éléments d’une liste.
for idx in range(len(a)):
...print idx, a[idx]
...
hello
1 world
S’il est nécessaire de parcourir une séquence tout en connaissant l’index de l’élément courant, la fonction enumerate est une solution plus «pythonesque».
for idx, val in enumerate(a):
...print idx, val
...
0 hello
1 world
Enfin, une chaîne étant une séquence de lettre, elle peut être parcourue comme une séquence.
for lettre in ’bar’:
...print lettre
...
Boucles avec condition
while <condition>:
<instructions>
else:
<instructions, condition fausse>
La boucle suivante représente la boucle minimale définissable avec un while. Certes, elle est stupide mais elle permet d’illustrer l’utilisation du Ctrl-C pour interrompre un traitement en cours d’exécution.