Introduction au langage Python3 - www.jchr.be
LE langage Python3 est un langage de programmation complet grâce aux nombreuses bibliothèques spécialisées. Utilisé dans l’industrie et dans la recherche, notamment par la NASA ou la recherche biomédicale, il est néanmoins idéal pour un apprentissage de la programmation, étant visuellement structuré. Cette page est réalisée pour les systèmes Unix
Orienté objet, il est également utilisable comme un bon vieux BASIC, moyennant quelques différences par rapport à ce dernier :
- certaines commandes basiques se trouvent dans des bibliothèques, modules qu’il faut importer : sys pour «quitter», time pour «attendre», math pour la trigonométrie, random pour le pseudo-aléatoire… Ces modules «internes» à importer ont été installés avec python. Mais contrairement au langage C, il n’est pas nécessaire d’importer de module pour les entrées et sortie de console ou de fichiers print() ou open()…
- les conditions et boucles sont structurées par indentation : pas de UNTIL, WEND ou ENDIF ni de { } pour structurer les blocs de condition ou de boucle
- les instructions graphiques sont l’objet de bibliothèques particulières, comme tkinter
- on ne dispose d’aucun GOTO ni GOSUB, remplacés par des fonctions ou procédures «utilisateur».
Le langage Python a été créé par Guido van Rossum, qui s’en est déclaré le «bienveillant dictateur à vie». Il est distribué sous une licence Open Source depuis la version 2.1. Cela signifie que tout le monde peut l’utiliser librement, consulter le code-source et le modifier, redistribuer la version modifiée – sans obligation d’y joindre les sources – contrairement à la licence GPL où les sources modifiées doivent être disponibles pour toute nouvelle version distribuée.
Cette page fut commencée en mai 2002 comme manuel de références personnel. Elle prétend maintenant faciliter la prise en main du langage Python en GNU+Linux, d’autres UNIX, voire d’autres systèmes. Cette page est remaniée au fil des versions, mais rien ne garantit que les corrections sont complètes.
Ces explications et informations peuvent vous aider – Vous êtes responsable de toutes utilisation qui pourraient endommager votre matériel ou vos données.
La dernière version de Python2 (2.7.18) date d’avril 2020, et Debian 12 Bookworm (juin 2023) ne l’a plus incluse dans ses dépôts. Une page consacrée à python2 vous aidera à adapter les anciens scripts en python3.
0. Pages liées à la présente1. Prise en main
1.1 Installation 2. Structure
2.1 Syntaxe |
3. Nombres
3.1 Types de variables numériques 4. Chaînes
4.1 Affectation 5. Collections
5.1 Tuples (,) |
6. Entrées et sorties
6.1 Affichage avec print() 7. Fonctions et modules «utilisateurs»
7.1 Fonction anonyme lambda 8. Programmation-objet
8.1 Classe et attributs 9. IntrospectionA. Documentation |
0. Pages liées à la présente
Quelques modules internes: système et fichiers, mathématiques, chaînes, bases de données simple.
Modules externes : numpy (tableaux - math - random) - ECMA-48 (print at en couleur)
L’interface graphique du module externeTKinter
Quelques recettes et les différences avec python2
1. Prise en main
1.1 Installation
Sur un système Unix normalement configuré, une version de python3 y est plus que probablement installée (par exemple, la version 3.9.2 pour Debian 11.0 Buster d’août 2021). Sinon, pour installer une application, on a l’obligation d’avoir les droits de super-utilisateur ("root")x. En ligne de commande, on utilise par exemple pour un système apt :
#apt install python3En mode super-utilisateur: su - [Enter]
Sous SuSE, Mandrake et Fedora, vous devriez pouvoir utiliser une ligne de commande du type :
#rpm -i python-3.7-19.6.i586.rpmEn mode super-utilisateur: su - [Enter]
Une solution est également d’utiliser l’installateur graphique de votre distribution.
Il peut être intéressant, dans un script, de connaître la version de python installée sur un système pour faire agir le code en conséquence. Cela se fait par le module sys :
>>> import sys ; print(sys.version_info) sys.version_info(major=3, minor=9, micro=2, releaselevel='final', serial=0)
1.2 Le mode interactif
Dans une console, on rentre dans le mode interactif en saisissant :
python3Mode simple utilisateur$
ce qui fait afficher quelque chose qui ressemble à :
Python 3.9.2 (default, Feb 28 2021, 17:03:44) [GCC 10.2.1 20210110] on linux Type "help", "copyright", "credits" or "license" for more information. >>>
Frappez 5 +3, puis [Enter] ou [Return] :
>>> 5 +3 8
En Unix, on quitte python en mode interactif avec quit() ou exit().
Il existe un mode interactif amélioré nommé ipython3. Il offre notamment un historique des portions entières de code (boucles, fonctions…) qu’il est possible de corriger sans devoir reprendre les lignes une à une. On quitte ce shell avec quit() ou exit().
1.3 Lancement d’un script de Python
Un script est un fichier de programmation, rédigé avec un simple éditeur de texte. Supposons que le fichier script.py se trouve dans le répertoire /home/toto/python/ et contienne :
# commentaire : mon premier programme Python print(5 +3)
Dans python interactif avec exec()
Il est possible de le lancer à partir d’une console où python3 a été lancé :
>>> exec(open("/home/toto/python/script/essai.py").read()) 8
Hors python interactif
Dans une console, frappez et saisissez :
$ python3 /home/toto/python/script.py
1.4 Lancement d’un script autoexécutable
Vous pouvez rendre le fichier /home/toto/python/script.py autoexécutable, à condition de savoir où se trouve l’application python. En UNIX, la commande whereis python3 renvoie (entre autres) une chaîne ressemblant à /usr/bin/python3. Un script autoexécutable devra alors commencer (toute première ligne) par
#! /usr/bin/python3
Modifier ensuite les propriétés du script (il doit être exécutable au moins pour le propriétaire) par la commande (en tenant compte du chemin réel du script) :
chmod o +x /home/toto/python/script.py
ou
chmod 740 /home/toto/python/script.py
ou par un gestionnaire de fichiers (le plus souvent par un clic droit sur le fichier). Il est dès lors possible de le lancer par la ligne de commande :
/home/toto/python/script.py
ou, si le fichier est dans le répertoire de la console (pwd) :
cd repertoire-du-fichier ./script.py
Note : en UNIX, il n’est pas possible de lancer un exécutable (dont les scripts) situés dans une partition si la ligne correspondante dans le fichier /etc/fstab comprend noexec dans la colonne <options>.
1.5 Lancement par le navigateur
Dans un navigateur de fichiers ou sur le bureau, double-cliquer sur un fichier .py lance normalement le script.
Ce genre d’appel ne permet pas de lire un message d’erreur lorsqu’une erreur interrompt le script. Lors de l’écriture, il vaut donc mieux lancer le script avec ./nom-du-script.py ou python3 nom-du-script.py dans une console.
Même sans erreur dans votre script, la console appelée lorsque vous double-cliquez sur un fichier .py se referme dès la fin du script. Il faudra penser à terminer ces scripts Python par input() ou une de ses sophistications :
out ="" while out !="q": # tant que le caractère n’est pas "q" out =input("[q] + [Enter] pour quitter! ")
1.6 Éditer les scripts
Il est possible d’utiliser les éditeurs de textes des systèmes graphiques, comme gedit / pluma (Gnome / MateDesktop), ou kwrite (KDE), qui disposent de la coloration syntaxique pour la plupart des langages de programmation, gedit / pluma, propose cinq modèles peu engageants mais qu’il est possible d’aménager (gedit / pluma).
Avec gedit / pluma, il est possible d’ajouter le greffon «Console python» par le menu Édition » Préférences » onglet "Greffons" » Console python ; l’accès se fait par le menu Affichage » Panneau inférieur ou [Ctrl F9]. Attention cependant, cette console intégrée n’est pas celui de la console lancée avec python3 dans la console «Terminal» de GNOME ou Mate : [Enter] passe à la ligne suivante (ce qui permet de coder sur plusieurs lignes et corriger en se repositionnant à la souris), seul [return] valide le code ; input() déclenche une erreur (pluma 1.26).
idle est un éditeur écrit en Python qui devrait faciliter votre prise en main sous interface utilisateur. La première fenêtre est le mode interactif. On peut ouvrir une fenêtre de programmation, écrire et lancer un script à partir de cette fenêtre, après l’avoir sauvegardé. Pour Windows, c’est probablement une meilleure solution que blocnote qui ne proposant pas la coloration syntaxique, mais il en existe d’autres.
Sous idle, [F1] affiche une page d’aide, par exemple http://www.python.org/doc/current/.
Il existe aussi spe , Stani’s python editor, qui installe 88Mo (non testé), et bien sûr vim et Emacs (qu’il faut bien connaître pour les utiliser).
Sur SailfishOS, python3 est nativement utilisable sur le terminal développeur.
La programmation en python avec l’interface android nécessite le chargement d’un module, ce qui mériterait une page à part (qui n’existe pas sur ce site). Il est néanmoins possible de lancer un script dans une console sur android (bien que son interface soit peu pratique pour un long développement).
et appuyer sur la flèche "play" (triangle orienté à droite). Si le script s’est bien déroulé, il suffit d’appuyer sur le retour-chariot du clavier virtuel. S’il y a eu une erreur, cliquer et fermer la console «No.1» en haut à gauche. Pour permettre des caractères non ascii dans les chaînes ou les commentaires, utiliser la déclaration utf-8 :
Il est possible de compiler un fichier-texte en un «bytecode». Ce nouveau fichier ne sera pas directement exécutable par le processeur, mais sera interprété plus rapidement par l’application python3, qui devra donc toujours être installée. Il conservera sa compatibilité avec les autres plate-formes, mais la compatibilité entre les différentes versions n’est pas garantie. Attention : le fichier .pyc généré par la compilation ne sera pas éditable : ne vous débarrassez pas de l’original .py.
Il y a plusieurs manières de compiler un fichier .py pour créer un fichier .pyc :
crée un répertoire __pycache__ contenant le fichier votreficher.cpython-3x.pyc (x selon la version de python3)
En mode interactif, en chargeant le module py_compile puis en lançant la compilation :
Une manière plus rapide est d’importer le fichier python (sans .py ni ponctuation dans le nom ; cette façon lancera le fichier compilé !) :
Il est à noter que cette compilation n’accélère que l’interprétation du fichier, mais pas les fonctions internes de python. Un fichier Python constitué d’une seule fonction qui exécuterait un remplacement de caractères sur un fichier texte de 100Mo ne serait en rien accéléré par cette compilation.
Dans une console, on lance ce fichier compilé avec python3 votrefichier.pyc
Le langage Python a une syntaxe simple et lisible :
Plutôt que les {} utilisées en C, PHP, javascript… on utilise des indentations pour marquer la structure. La définition de la fonction fermat() s’arrête au moment où le script se réaligne sur la colonne où commence def. De la même manière, la condition contenue dans la fonction connaît également une indentation. Les lignes vides sont facultatives et servent à mieux structurer le script.
A priori, les instructions se déroulent selon l’ordre dans lequel elles ont été écrites, sauf si une fonction a été définie. Lorsque l’interpréteur lit le script, il prend note de la fonction, qu’il n’exécute que lorsque le script y fait appel. Une portion de code peut être exécutée sous condition ou un certain nombre de fois, prédéfini (for) ou non (while).
Une variable contient une valeur ou plusieurs valeurs qui peu(ven)t être redéfinie(s) autant de fois que l’on veut, d’où le nom. Un ensemble de lettres minuscules ou majuscules, accentuée ou non, idéogramme ou caractères thaï, de chiffres et de tirets bas "_" (mais pas de trait d’union), ne commençant pas par un chiffre, peut représenter une variable (ou définir une fonction) :
del Qio ou del(Qio) supprime la variable Qio, de quelque type que ce soit.
Certains mots sont réservés à python et ne peuvent donc pas être utilisés comme nom de variable ou de fonction : and - as - assert - break - case - class - continue - def - del - elif - else - except - finally - for - from - global - if - import - is - lambda - match - not - or - pass - raise - return - try - while - with - yield
Les classes internes (voir introspection) bool, int, float, complex, str, bytes, tuple, list, dict, set, map, range, filter, type, zip , enumerate ne sont pas réservées, mais il vaut mieux éviter de les utiliser.
Les noms de fonction (sans parenthèses) ne sont pas réservés non plus, mais l’utilisation comme variable d’un nom de fonction rend celle-ci indisponible :
type() renvoie la classe (type d’objet) d’une variable. Par exemple, type(["eggs", 3]) renvoie <class 'list'>. Voir Introspection.
Le première utilisation d’une variable doit être une assignation, qui peut se faire de plusieurs manières : A =13, for i in [2,3,5] : ou def fonct(i =0, j, k) :
Attention ! une variable définie n’est pas nécessairement disponible dans toutes les parties d’un script : voir fonctions.
Il est possible d’effectuer une permutations des valeurs de variables, qui peuvent être de types différents (numériques, chaînes, listes…) :
Pour inscrire une chaîne parmi les variables du système, voir sys.intern().
Certaines variables propres à python se trouvent dans certains dictionnaires, voir introspection.
Il existe deux valeurs booléennes (du mathématicien et logicien George Boole, 1815-1864) : True et False (Capitale + minuscules !).
0, 0., 0 +0j, None, "", () , [] et {} peuvent représenter la valeur False :
bool() retourne False si l’argument est 0, 0 +0j, "", None, [], {}, bien que ces expressions ne sont pas tout à fait équivalentes à False
Toute autre valeur ou toute collection non vide (chaîne, tuple, liste, dictionnaire ou ensemble peuvent représenter la valeur True
bool() retourne un True si l’argument est différent de 0, 0 +0j, "", None, [], {}. Attention : [[]], "0" et [0] sont True !
Les valeurs booléennes résultent souvent de l’évaluation d’une relation d’égalité, d’inégalité ou d’inclusion, qui sont souvent utilisées dans les structures conditionnelle (if et while).
Comme les langages C et dérivés, Python fait une différence entre l’opérateur d’assignation a =2, qui attribue une valeur à une variable, et l’opérateur de comparaison a ==3, qui sert à tester l’égalité :
L’opérateur «est différent de» est noté !=
Les autres tests d’inégalité sont (commencent toujours par < ou >) :
< plus petit que, ou inclusion pour les ensembles
Il est possible d’enchaîner les comparaisons :
Les opérateurs d’inégalités ne sont pas définis pour les dictionnaires, mais peuvent comparer des listes à condition que les valeurs de chaque rang soient comparables, et qu’elles ne contiennent pas de dictionnaire :
Pour les ensembles, les opérateurs de comparaison signifie l’inclusion (<), la contenance (>) et l’égalité (==).
True et False valent respectivement 1 et 0 dans les opérations arithmétiques :
De ce fait, True peut valider une chaîne par multiplication ; False l’annihiler :
a is b est vrai si les valeurs ET les classes (type d’objet) sont égaux (voir variables numériques). De plus, id(a) équivaut à ce moment à id(b).
a is not b est vrai si les valeurs OU les types sont inégaux : int(1) is not float(1) est vrai
Les opérateurs de comparaison évaluent également les chaînes de caractères, <, <=, >, >= selon l’ordre alphabétique de leur code ASCII :
' ' < "0" < "1"… < "A" < "B"… < "a" < "b"… (voir
caractères spéciaux). Les chaînes disposent en outre de méthodes qui leur sont propres.
chaine1 in chaine2 est vrai si la première chaîne est contenue dans la seconde
li1 in li2 est vrai si li1 est un élément de li2
Le comparateur in fonctionne également pour les éléments d’un ensemble ou pour les clés d’un dictionnaire :
Les expressions ci-dessus peuvent se composer :
expr1 or expr2 est vrai si l’une des deux est vraie (la seconde ne sera évaluée que si la première est fausse)
Attention : les expressions contenant or et and renvoient le dernier nombre évalué. Quand l’interpréteur rencontre l’expression 13 or 56, il se dispense d’évaluer 56, 13 étant différent de 0 et donc déjà vrai ; en rencontrant 13 and 56, l’interpréteur doit par contre évaluer le 56 puisque le premier terme vrai ne suffit pas : 3 and 5 vaut 5 car le second terme a dû être évalué, 5 and 3 vaut 3. 0 or 3 vaut 3 puisque ce dernier a dû être évalué.
Note : Pour les comparaisons bit à bit de deux nombres, utiliser &, |, ^ et ~ (voir Opérations sur les bits)
Une condition permet ou non le déroulement d’une portion de code. Un exemple sera plus éloquent qu’une explication a priori (notez l’indentation des parties de code à considérer ou non selon la condition) :
if présente ce qui doit être fait si la condition qui le suit est rencontrée, elif permet de préciser une alternative soumise à condition ; else permet une alternative si aucune des conditions (if ou elif) n’a été rencontré. elif et else ne sont pas obligatoires et l’un peut être utilisé sans l’autre, mais toujours avec if.
Il est possible d’inclure une condition dans une autre :
Il est souvent possible de simplifier ces tests de parité :
Parfois, la condition est bien cachée (a %2 vaut 0 et annule la chaîne im) :
Notes :
La boucle teste une condition avant d’exécuter le bloc d’instruction indenté, et celui-ci sera répété tant que la condition sera vraie. Dans l’exemple suivant, la variable i sera incrémentée (augmentée) de 1 et imprimée jusqu’à ce qu’elle atteigne 10.
Note : while True :, while 23 : ou while not(0) : est toujours vrai et génère une boucle sans condition de sortie. On en sort généralement avec une condition amenant à break.
break quitte immédiatement la boucle en évitant else
pass ne fait rien, mais est parfois nécessaire dans une syntaxe, par exemple pour forcer une indentation ou un réalignement.
for variable… in collection permet de considérer l’un après l’autre chaque élément d’une collection :
…la collection étant dans ce premier exemple un tuple (sans parenthèse dans ce cas-ci), tuple pouvant être lui-même être composite. La boucle classique du BASIC FOR I = x TO y utilise la liste produite par range :
n prend successivement toutes les valeurs de 0 à 9 (compris). Les instructions du bloc seront toutes activées 10 fois, et le programme continuera ensuite après le bloc défini sous la boucle for.
Il est possible de fixer un début, une fin et un pas, nécessairement négatif si le début excède la fin :
La succession de valeurs prises par la variable peut également provenir
d’une chaîne :
On peut imbriquer les boucles (notez les indentations successives) :
Une boucle très simple peut s’écrire en une ligne, mais ne peut être précédée de quoi que ce soit :
<<< a =1
<<< for i in range(1,5) : a *=i ; print(a)
Ceci ne fonctionnera pas :
<<< a =1 ; for i in range(1,5) : a *=i ; print(a)
continue retourne directement au début de la boucle, ce qui permet d’économiser les indentations :
devient
ce qui est appréciables dans les boucles longues et imbriquées.
else : introduit une séquence qui sera parcourue lors de la sortie normale de la boucle
break quitte immédiatement la boucle en évitant else
pass ne fait rien, mais est parfois nécessaire dans une syntaxe, par exemple pour forcer une indentation ou un réalignement. Les deux boucles suivantes servent à se convaincre que lorsque deux boucles numériques sont imbriquées, on gagne un peu de temps à mettre la plus petite dans la grande :
iter() permet la construction d’un objet itérable à partir d’une chaîne, liste, tuple ou dictionnaire, utilisable par la boucle for :
Il est possible d’utiliser iter qui appelle lui-même une fonction, à condition de donner une valeur-sentinelle qui stoppera la boucle lorsqu’une même valeur sera rencontrée. Dans le cas qui suit, la fonction hasard() retourne aléatoirement un nombre de 0 à 4 ; la boucle s’arrêtera au premier 3 fourni (qui ne sera pas imprimé).
match / case est apparu avec python3.10. match présente la valeur à comparer, chaque cas étant introduit par case, qui peut regrouper plusieurs propositions séparées par | . Le cas «non rencontré» est symbolisé par le souligné _
match teste la relation d’égalité et peut remplacer un if … elif … else, moins lisible :
…mais if / elif / else est plus souple, pouvant également tester d’autres relations comme les inégalités.
Il est également possible d’envoyer un signal suivi d’éléments réceptionnés par un tuple préfixé de * :
with permet une opération contextuelle, limitée à cette structure. as (facultatif) permet de récupérer la valeur produite par l’opération qui suit with, par exemple le descripteur de fichier.
On voit par cet exemple que le descripteur de fichier fd généré par l’ouverture du fichier n’est valable qu’au sein de la structure, python fermant proprement le fichier en fin de structure, même si le script est interrompu dans la boucle (pour les détails sur les fichiers, voir 6.3 Fichiers texte).
Note : il est possible de créer des objets utilisables dans cette structure à condition de les doter des méthodes __enter__() et __exit__()
Plutôt que de subir une erreur, il est possible de tester une expression, de voir la réaction de l’interpréteur et de faire réagir le programme de façon adéquate avec except. else, facultatif, introduit une portion de code qui ne sera parcourue que s’il n’y a pas d’erreur.
Il est possible, et recommandé, de préciser une erreur particulière, ou plusieurs (dans, un, tuple) :
Il est à noter qu’une simple condition suffit dans ce cas :
Sans spécification de l’erreur attendue (voir listes des erreurs), except masque les autres erreurs possibles. C’est pour cette raison que certains déconseillent la séquence try / except car elle peut rendre le débogage plus complexe.
finally (facultatif) introduit une portion de code qui sera parcourue dans tous les cas, erreur ou pas erreur.
raise KeyboardInterrupt produit l’exception précisée, par exemple pour une mise au point :
assert produit l’exception particulière AssertionError en cas de test négatif, remplaçant la séquence if test : raise This_Error. On peut lui passer une valeur (chaîne, nombre, collection, calcul, fonction...) :
Notes
Les types d’erreurs définis dans le module exceptions.py :
Et voici les avertissements :
var =43 permet d’assigner une valeur à une variable numérique. Les entiers sont illimités.
nbr =2.65 est une variable «réelle» (en informatique, «réel» signifie "non entier")
Les «réels» ont une mantisse de 16 ou 17 chiffres, l’exposant va de -308 à 308 (cela dépend également du nombre de chiffres utilisés pour la mantisse). Pour fixer le nombre de chiffres pour la mantisse, voir le module decimal.
c =3 +4.1j est un nombre complexe, j ou J postposé qualifie la valeur imaginaire
Notes : les nombres complexes refusent les relations d’ordre <, >1.7 Mode console sur smartphones 2015.08
#qpy: console
print("bonjour le monde")
# qpy: console
# -*- coding: utf-8 -*-
print("hélas le monde!")
1.8 La compilation
>>> python3 -c "import votreficher.py"
>>> import py_compile
>>> py_compile.compile("votrefichier.py")
>>> import votrefichier
2. Structure
2.1 Syntaxe
#! /usr/bin/python3
def fermat(n1, n2) :
"""teste si un couple d'entier est une paire de Fermat"""
rep =(n1 **2 + n2 **2) **.5
if rep ==round(rep) : # si la reponse est un entier
return f"{n1} et {n2} forment une paire de Fermat: {n1}² +{n2}² ={round(rep)}²"
else : # sinon
return f"{n1}² +{n2}² = {rep}² et ne forment pas une paire de Fermat"
print(fermat(5, 12))
print(fermat(7070, 7071))
2.2 Variables et assignations
Cette section est générale pour les différents types de variables : numériques, chaînes, tuples, listes, dictionnaires et ensembles.
Mots réservés
>>> print(5)
5
>>> print =5
>>> print(5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable
Assignation de variable
>>> x =4 ; y ="trois" ; z =["a", "b"]
>>> x, y, z =y, z, x
>>> x, y, z
('trois', ['a', 'b'], 4)
2.3 Booléens et comparaison
>>> if not {} : print("vrai")
…
vrai
>>> {} == False
False
>>> print(1 >2, 3 ==3, 1 in [0, 1, 2], {1, 2} > {1, 2, 4})
False True True False
>>> a =2 # la valeur 2 est assignée à la variable a
>>> a ==3 # comparaison de la variable a (qui vaut deux) et la valeur 3
False
>>> a =2 ; a !=3
True
<= plus petit ou égal à
> plus grand que, ou contenance pour les ensembles
>= plus grand ou égal à
>>> 1 < 3 > 2
True
>>> a =[1, "a", [1, 2 ,3], False] ; b =[5, "z", [], True]
>>> a < b
True
>>> a =3 ; print((a ==3) *5 +True)
6
>>> nb =3 ; print(nb, "oeuf" +"s" *(nb > 1), " et spam"*False)
3 oeufs
Égalité et identité
>>> a =123 ; b =123 +0j ; a ==b ; a is b
True
False
Appartenance avec in
chaine1 not in chaine2 est vrai si la première chaîne n’est pas contenue dans la seconde
li1 not in li2 est vrai si li1 n’est pas un élément de li2
>>> ens= {1, 2, 3} ; 2 in ens
True
>>> dic ={"a": 1, "b":2} ; "c" in dic
False
Calcul booléen
expr1 and expr2 est vrai si les deux sont vraies (la seconde ne sera évaluée que si la première est vraie)
not est la négation logique : not(1 ==2) est vrai
2.4 Conditions : if expr: … elif expr : … else : …
#! /usr/bin/python3
i =4 # on peut faire varier cette valeur
if i<5 :
print(i, "est plus petit que 5")
elif i ==5 :
print(i, "est exactement égal à 5")
else :
print(i, "dans les autres cas, si i est plus grand que 5")
print("C'est fini") # ceci est hors condition, puisque sans indentation
#! /usr/bin/python3
a =-3
if a ==0 :
print("=0")
elif a < 0 :
print("< 0")
else :
print("> 0")
#! /usr/bin/python3
i =7
if i<0 :
if i%2 ==0 :
print(i,"est négatif et pair")
else :
print(i,"est négatif et impair")
else :
if i%2 ==0 :
print(i,"est positif et pair")
else :
print(i,"est positif et impair")
if i %2 : # pour un i pair, la réponse est "0", valeur réputée fausse
print("i est impair")
else :
print("i est pair")
a =1 ; print(a, "est", "im" *(a %2) +"pair")
2.5 Boucle while
i =0
while i<11 :
print(i)
i +=1 # équivaut à i =i +1
while True :
a =input("[q] + [return] pour quitter: ")
if a =="q" : break
print("Réessayez")
continue retourne directement au début de la boucle, ce qui permet d’éviter des tests de condition inutiles
nb =9
while nb !=0 :
nb =input("0, 1, 2 ou 3? ")
if nb ==1 : print("Choix un") ; continue
if nb ==2 : print("Choix deux") ; continue
if nb ==3 : print("Choix trois")
2.6 Boucle for + iter()
for chaine in "bacon", 43, ["eggs","bacon"], {43:"eggs", "bacon":87} :
print(type(chaine), chaine)
for n in range(10) : # range(10) génère les valeurs de 0 à 9
print(n, n *n)
>>> for var in range(10,4,-2) :
>>> print(var)
10
8
6
for i in "Python" :
print(i)
for i in range(1,11) :
print("Table de multiplication par", i)
for j in range(1,11) :
print(i, "x", j, "=", i *j)
print() # ajoute de ligne entre chaque table
print("C'est tout")
Instructions associées
chaine ="Le python est un langage élégant"
for i in chaine :
if i !=" " :
print(i)
chaine ="Le python est un langage élégant"
for i in chaine :
if i ==" " : continue
print(i)
import time
t =time.thread_time()
for i in range(100) :
for j in range(1000000) :
pass
print(time.thread_time() -t)
t =time.thread_time()
for i in range(1000000) :
for j in range(100) :
pass
print(time.thread_time() -t)
a =iter([1,2,3])
for i in a :
print(i)
import random
def hasard() :
return random.randrange(5)
for i in iter(hasard, 3) :
print(i)
2.7 match / case Nv 2023.06.21
q =input("Initiale d'une couleur primaire: ")
match q.lower() :
case "m" :
print("magenta")
case "j" :
print("jaune")
case "c" :
print("cyan")
case "b" | "r" | "v" :
print("rouge, vert et bleu sont les couleurs fondamentales")
case _ :
print("La réponse ne convient pas")
q =input("Initiale d'une couleur primaire: ").lower()
if q =="m" :
print("magenta")
elif q =="j" :
print("jaune")
elif q =="c" :
print("cyan")
elif q in "b", "r", "v" :
print("rouge, vert et bleu sont les couleurs fondamentales")
else :
print("La réponse ne convient pas")
#! /usr/bin/python3
def faire(x) :
match x :
case ["K", *z] :
print("Quelques carrés:")
for i in z :
print(i **2)
case ["Q", *z] :
print("Quelques cubes:")
for i in z :
print(i **3)
faire(["K", 2, 3, 7])
faire(["Q", 3, 8, 9, 11])
2.8 Structure with + as
with open("essai.txt") as fd :
print(fd.read()) # sortie console du fichier ouvert, uniquement dans la structure "with"
print(fd.read()) # hors structure, génère 'ValueError: I/O operation on closed file'
2.9 Traitement des erreurs
try / except / else / finally
try :
1/0
except :
print("Une erreur est apparue")
else :
print("Tout va bien")
finally :
print("Avec ou sans erreur")
for i in range(-5, 6) :
try :
1 /i
except ZeroDivisionError :
print(" -")
else :
print("%.3f" %(1 /i))
for i in range(-5, 6) :
if i ==0 :
print(" -")
else :
print("%.3f" %(1 /i))
raise, assert
>>> raise KeyboardInterrupt("Commmentaire facultatif")
Traceback (most recent call last) :
File "<stdin>", line 1, in ?
KeyboardInterrupt: Commmentaire facultatif
>>> assert 2 < 1, "Mon message d'erreur"
AssertionError: Mon message d'erreur
2.A Liste des erreurs
3. Nombres
3.1 Types de variables numériques
Pour les généralités sur les variables, voir la section variables et assignations.
nbr =265e-2 équivaut au précédent : 265 *10 **-2 =265 /100
c.real renvoie la partie «réelle» du complexe
c.imag renvoie la partie imaginaire du complexe
c.conjugate() renvoie le nombre complexe conjugué (a +bj et a -bj sont des complexes conjugués)
Python calcule dans l’ensemble des complexes (ici, la racine complexe d’un complexe) :
>>> (1 +4j) **(1 /(2 +3j)) (1.6758516946016602-0.20706889169199882j)
Types et conversions
print(type(var)) renvoie <class 'int'>, <class 'float'> ou <class 'complex'> selon la variable. Voir Introspection.
Il existe un type de nombre permettant d’autres valeurs (l’infini…), voyez le module Decimal
int() transforme un nombre ou une chaîne conforme en entier illimité. Les «réels» sont arrondis par défaut
float() transforme un nombre ou une chaîne conforme en «réel»
complex() transforme des nombres, des variables ou une chaîne conforme (pas d’espace) en nombre complexe
>>> x =3.7 ; y =4.1 ; z =complex(x, y)
3.2 Opérateurs numériques
= assigne une valeur à une variable : A =5, b =A
== test d’égalité entre variable(s) et valeur, qui retourne True ou False
+ additionne nombres et variables numériques
- soustrait une valeur à une autre ou à une variable
* multiplie deux nombres ou variables numériques
// permet la division entière, valable pour des «réels» ; attention aux résultats négatifs :
/ une division donne une réponse «réelle» :
>>> 7 /4 1.75
…même quand elle tombe «juste» :
>>> 32 /8 4.0
>>> 7.7 //4 1.0 >>> -7 //4 -2
Veillez donc à utiliser // quand vous désirez que la réponse soit un entier.
x %y ("x modulo y") renvoie le reste d’une division : 7 %3 donne 1. Le résultat est toujours positif ou nul : -3 %7 renvoie 4
divmod() division renvoie une paire de nombres, le résultat entier et le reste :
>>> divmod(3,.7) (4.0, 0.20000000000000018)
divmod(x,y)[0] équivaut à x //y
divmod(x,y)[1] équivaut à x %y
Une manière simple est d’utiliser un tuple pour recevoir les réponses :
resultat, reste =divmod(x,y)
** marque l’exposant et a la priorité sur +, -, *, / (Attention : ^ est l’opération «et» sur les bits)
pow(x,y) renvoie x à la puissance y, équivaut à x **y
pow(x,y,z) vaut x **y %z
Ces opérateurs s’organisent entre eux comme on a appris à l’école :
>>> (7 -5) *(3 +2) **2 donne (2 *5) **2 (=100)
Dans les formes x +=y, x -=y, x/=y, x//=y, x *=y, x **=y, x%=y… le premier opérande reçoit le résultat de l’opération :
>>> x =3 ; x +=2 ; x 5
abs() renvoie la valeur absolue d’un nombre (sans le signe)
abs(a +bj) renvoie le module du vecteur exprimé en coordonnées cartésiennes (l’hypothénuse du triangle rectangle dont les deux autres côtés sont a et b).
round(x, dec) arrondit un «réel» (précision jusqu’à la 15e décimale). n négatif permet un arrondi à la dizaine, centaine, au millier… près.
>>> round(3.14159, 2) ; round(2143, -2) 3.14 2100
round(x) arrondit un «réel», par excès (vers l’entier supérieur) à partir de .5 si la partie entière est impaire, et par défaut (vers l’entier inférieur) si la partie entière est paire.
>>> round(6.5) ; round(7.5) 6 8
Explication : dans un traitement d’un grand nombre de réels, il y a pertes de valeurs dans quatre cas (pour les décimales 0.1 à 0.4) compensées par les gains (pour les décimales 0.6 à 0.9). Un arrondi systématique de 0.5 par excès amènerait donc en moyenne un biais de +0.5 pour dix nombres
max(x, y) renvoie la plus grande des deux valeurs
min(x, y) renvoie la plus petite des deux valeurs
cmp() est supprimé
cmp(x, y), qui renvoie -1 si x <y , +1 si x >y et 0 si x ==y, est supprimée en python3. Elle est facile à écrire soi-même, voir la discussion et recette sur cette page.
Voir également le module math pour les fonctions mathématiques plus avancées sur les nombres, dont la fonction math.copysign(a, b) qui renvoie la valeur de a munie du signe de b.
3.3 Bases numériques
Même si l’on utilise généralement la base décimale (même sans le savoir), il en existe d’autres, où chaque rang vers la gauche représente une multiplication par une autre valeur que 10. Cela ne concerne qu’une manière d’encoder un même nombre.
Octal
L’octal (qui ne fonctionne qu’avec des entiers) est une base numérique où les chiffres ont des valeurs possibles de 0 à 7. Un nombre sous forme de chaîne octale commence par 0o : 0o253 vaut 2 *8 *8 +5 *8 +3 (= 171). oct() transforme un entier en chaîne octale :
>>> oct(43) '0o53'
Pour transformer une chaîne octale en entier, il faut utiliser int("", 8) :
>>> int("0o53", 8) 43
Par défaut, une valeur est exprimé en décimal :
>>> print(0o53) 43
Notes : le formatage "%o" %(n) permet une transformation en chaîne :
>>> print("%d s'écrit %o en octal" %(43, 43)) 43 s'écrit 53 en octal
Hexadécimal
L’hexadécimal (qui ne concerne que les entiers) est une base numérique où les chiffres ont des valeurs possibles de 0 à 15, codées avec les chiffres de 0 à 9 et les lettres de a à f (valeurs 10… 15). Les nombres sous forme de chaîne hexadécimale commence par 0x ou 0X . 0x2b vaut 2 *16 +11 (=43)
hex(entier) retourne un nombre en chaîne hexadécimale :
>>> hex(43) '0x2b'
Pour transformer une chaîne hexadécimale en entier, il faut utiliser int("", 16) :
>>> int("0x2b", 16) 43
Binaire
bin() permet de transformer un nombre en chaîne binaire, qui commence avec le préfixe 0b… et ne comprenant que des 0 et des 1 : 43 =0b101011
>>> bin(43) '0b101011' >>> print(0b101011) 43
Décimal
int(chaine) et long(chaine) convertissent une chaîne énoncée en base 10 en un nombre
int(chaine, base) permet de préciser une base, entre 2 et 36
>>> int("123456") 123456 >>> int("2b", 16) # le préfixe n’est pas nécessaire 43
La chaîne ne peut pas contenir de "chiffre" plus grand que ceux admis par la base. En base 36, les chiffres conventionnels et les 26 lettres de l’alphabet peuvent être utilisés, z ou Z représentant la valeur 35.
3.4 Opérations sur les bits
var << n décalage des bits vers la gauche (équivaut à var *2 **n)
var >> n décalage des bits vers la droite (équivaut à var //2 **n)
Remarque : l’exposant a priorité sur la multiplication et la division.
var1 & var2 «et» logique : 1 si les deux bits correspondants valent 1
…001100 (12) …000101 (5) …000100 (4, résultat)
var1 | var2 «ou» inclusif : 1 si au moins 1 des deux bits correspondants vaut 1
…001100 (12) …000101 (5) …001101 (13, résultat)
var1 ^ var2 «ou» exclusif : 1 pour les bits correspondants différents
…001100 (12) …000101 (5) …001001 (9, résultat)
~var inversion de chaque bit d’une variable, ~nbr équivaut à -(nbr +1) : uniquement valable pour les entiers
…000010 (2) …111101 (-3, résultat; ..1111 vaut -1, ..1110 vaut -2, ..1101 vaut -3)
Rappel ! (voir fin de Comparaisons et booléens)
- 13 | 56 (=61) n’équivaut pas à 13 or 56 (=13)
- 13 & 56 (=8) n’équivaut pas à 13 and 56 (=56)
4. Chaînes de caractères
Pour les généralités sur les variables, voir la section variables et assignations.
Une chaîne est une collection ordonnée de caractères. Aux débuts de la micro-informatique, seuls étaient disponibles les caractères non accentués majuscules et minuscules, chiffres, opérateurs numériques ainsi que les signes de ponctuation les plus usuels (code ASCII arrêté à l’octet 127). Certains codages ont ensuite permis les lettres accentuées ou certains alphabets non latins (iso-8859). Avec la généralisation de l’UTF-8, l’ensemble des caractères Unicode est disponible, si toutefois le système s’exploitation dispose des polices pour les afficher.
4.1 Affectation de chaînes
ch ="python" la variable ch reçoit la valeur "python"
ch ="C'est bien" une apostrophe peut être inclue dans une chaîne délimitée par des guillemets doubles
V ='Un "faux"' des guillemets doubles peuvent être inclus dans une chaîne délimitée par des guillemets simples (apostrophes)
V ='C\'est "faux"' le caractère \ permet d’échapper («protéger») l’apostrophe dans une chaîne déterminée par des apostrophes
V ="C'est \"faux\"" le caractère \ permet d’échapper («protéger») les guillemets doubles dans une chaîne déterminée par des guillemets doubles
V ='123' la variable V reçoit une chaîne de trois chiffres
chaine ="""Trois doubles guillemets permettent une chaîne en plusieurs lignes sans se "préoccuper" des 'guillemets' '''chaine''' fonctionne aussi"""
Ces trois guillemets doubles permettent de «commenter» (désactiver) une série de lignes de code alors que # ne permet que de commenter le reste de la ligne courante. Le premier groupe de trois doit être aligné sur l’indentation du bloc.
for i in "abcd" : a ="oui" ; b ="non" ; c ="peut-être" """ print(a) print(b) """ print(c)
Le première utilisation d’une variable doit être une assignation, qui peut se faire de plusieurs manières : A ="", for i in "SPAM" : ou def eggs(ch1 ="", arg) :
len("python") renvoie la longueur d’une chaîne
type(expr) renvoie <class 'str'> si l’expression est une chaîne. Voir Introspection.
chr(n) affiche un caractère à partir de son code numérique
ord("c") affiche le code numérique des caractères disponibles (Unicode)
Les caractères de contrôle, dont le code numérique est inférieur à 32, doivent être codés :
chr(7) ou \a est censé donner un bip (dépend du BIOS?)
chr(8) ou \b revient d’un caractère à gauche
chr(9) ou \t saute à la tabulation suivante loin (colonnes octuples)
chr(10) ou \n en UNIX et MacOSX : retour chariot (première colonne, une ligne plus bas) ; DOS et MacOS9 : une ligne plus bas
chr(11) ou \v tabulation verticale (en fait une ligne plus bas, sans changer de colonne
chr(12) ou \f une ligne plus bas, sans changer de colonne
chr(13) ou \r en UNIX, MacOSX et DOS : retour à la colonne 0, sans changer de ligne ; MacOS9 : retour-chariot
chr(27) introduit les codes d’échappement, voir ECMA
Les autres caractères dont le numéro d’ordre est inférieur à 32 ne seront probablement pas reconnus par un simple éditeur de texte, voire empêcheront le chargement du fichier TXT (gedit, pluma).
chr(92) ou \\ est nécessaire pour coder le caractère \
\x.. est utilisé pour coder un octet par son code heXadécimal, voir bytes
repr(objet) représentation d’un objet sous la forme de chaîne avec les échappements pour qu’on puisse l’afficher avec print
>>> a ="C'est déjà ça" >>> a ; print(a) "C'est déjà ça" C'est déjà ça >>> repr(a) ; print(repr(a)) '"C\'est déjà ça"' "C'est déjà ça"
r"chaine" permet le codage d’une chaîne littérale, où les codes d’échappement \ ne sont pas évalués. Comparez :
>>> print("a\nb\tc") a b c >>> print(r"a\nb\tc") a\nb\tc
repr() et str() transforment des objets en une représentation en chaîne, voir section suivante.
Il existent également les chaînes de formatage f"{:}"
4.2 Opérations sur les chaînes
Voir aussi le module 3.1 string
= assigne une valeur à une variable : chaine ="azerty"
+ prolonge une chaîne par une autre chaîne (concaténation) :
>>> ch1 ="ah " ; ch2 ="bon!" ; ch1 +ch2 ah bon!
Il est possible d’effectuer une permutation des valeur de variables, qui peuvent être de types différents (numériques, chaînes, listes…) :
>>> x ="un", y ="deux", z ="trois" >>> x, y, z =y, z, x >>> x, y, z
* multiplie une chaîne par un entier
>>> _q ="ah" ; _q *3 ahahah
ch[3] ou "chaîne"[3] désigne le quatrième caractère de la chaîne
>>> a ="chaîne" >>> for i in range(len(a)) : >>> print(i, a[i]) 0 c 1 h 2 a 3 î 4 n 5 e
chaine =chaine[debut:fin] sélectionne une sous-chaîne à partir de la position debut (la première est 0)
jusqu’à la position fin non comprise (la dernière est -1, l’avant-dernière -2…)
chaine[m:n:i] renvoie une chaîne composée du caractère m, puis tous les i caractères jusqu’au caractère n-1
chaine[0:] équivaut à la chaîne elle-même
"abaca".count("a") compte le nombre de "a" dans la chaîne
"abaca".count("a", 1) …à partir de l’élément à la position 1 (le "b")
str() convertit un objet en chaîne :
>>> str(0.1) ; str([3, 1, 4]) ; str({"a":1}) ; str(set([0.707, 1.823])) '0.1' '[3, 1, 4]' "{'a': 1}" '{0.707, 1.823}'
repr() convertit également un nombre en chaîne :
>>> repr(0.1) ; repr([3, 1, 4]) ; repr({"a":1}) ; repr(set([0.707, 1.823])) '0.1' '[3, 1, 4]' "{'a': 1}" '{0.707, 1.823}'
La nuance est paraît-il subtile. Voir ici
La fonction eval() tente d’interpréter une chaîne en objet python :
>>> li =eval("[1 *2, 3 *4]") ; li [2, 12]
li =chaine.split() découpe une chaîne à chaque espace, tabulation, fin de ligne… et place les éléments dans une liste
li =chaine.split(ch) découpe une chaîne selon un caractère ou chaîne et place les éléments dans une liste, ch ne peut être la chaîne vide
li =chaine.split(ch, n) découpe une chaîne un nombre limité de fois
Notes :
- "a-b--c".split("-") renvoie ['a', 'b', '', 'c'], espace vide compris
- "a b c".split(" ") avec une double espace fait de même : ['a', 'b', '', 'c']
- "a b c".split() supprime les doubles espaces avant la découpe : ['a', 'b', 'c']
- list("ab c") transforme une chaîne en liste de caractères : ['a', 'b', ' ', ' ', 'c']
li =chaine.rsplit(ch, n) découpe une chaîne un nombre limité de fois à droite, sans renverser l’ordre de la liste
chaine ="".join(liste) assemble dans une chaîne les éléments d’une liste de chaînes
chaine ="-".join(liste) assemble dans une chaîne les éléments d’une liste de chaînes séparés par des traits d’union (ou tout autre caractère ou toute autre chaîne)
ch.strip() retire espaces, tabulations, sauts de ligne… en début et fin de chaîne (pas à l’intérieur !) :
>>> "\v\t chaîne entourée \r \n\f".strip() 'chaîne entourée'
filter(fonction, collection) crée un filter object qui recueille les éléments d’une collection (par exemple les caractères d’une chaîne) selon une condition en utilisant une fonction, éventuellement créée localement avec lambda ou définie par l’utilisateur. Dans cet exemple, chaque lettre d’un mot voit son rang ASCII/Unicode évalué, qui doit se situer entre 111 et 117.
>>> list(filter(lambda x: 111 < ord(x) < 117, "python")) ['p', 't']
La fonction doit retourner une valeur booléenne True ou False, qui permet la sélection de l’élément. Ici, ce sont les mots de longueur impaire (len(x) %2 est alors différent de 0, équivalent à True) qui sont sélectionnés :
def impair(x) : return len(x) %2 print(list(filter(impair, ("egg", "spam", "monty", "python"))))
La fonction filter() peut souvent être remplacée par une condition dans une liste en compréhension :
>>> [x for x in "python" if 111 < ord(x) < 117] ['p', 't'] >>> [x for x in ("egg", "spam", "monty", "python") if len(x) %2] ['egg', 'monty']
Si l’expression est entourée de [], il s’agira d’une liste ; entre (), d’un objet generator, itérable.
Tests sur les chaînes
Quelques tests renvoient un booléen True or False, unicode géré selon le paramétrage du script (par défaut pour python3). Voir le sous-module curses.ascii, limité aux caractères ASCII (ord < 128).
print("é".isalpha()) # True print("1".isdigit()) print("1".isalnum()) # réunit les deux précédents print("e".islower()) print("A".isupper()) print(" ".isspace()) # True si " " (\x20), "\t" (\x09), "\n" (\x0a), "\v" (\x0b), "\f" (\x0c), "\r" (\x0d), mais pas "\b" (\x08)
Remarque: isupper() renvoie False pour les majuscules grecques polytoniques ᾼ ῌ et ῼ ετ composées.
4.3 Comparaison et tris de chaînes
ch1 == ch2 est vrai si les deux chaînes sont identiques
ch1 != ch2 est vrai si les deux chaînes sont différentes
ch1 < ch2 est vrai si la chaîne ch1 est classée avant ch2, ce qui signifie que le premier caractère de la première est classé ord()avant le premier caractère de la seconde chaîne ; en cas d’égalité, le second caractère est considéré, etc.
ch1 > ch2 est vrai si la chaîne ch1 est classée après ch2
min(ch1, ch2, ch3…) renvoie la chaîne qui se classe alphabétiquement avant la ou les autre(s)
max(ch1, ch2, ch3…) renvoie la chaîne qui se classe alphabétiquement après la ou les autre(s)
max(chaine) renvoie le caractère de la chaîne dont le code numérique est le plus élevé
min(chaine) renvoie le caractère dont le code numérique est le moins élevé
Attention : cmp(ch1, ch2) qui renvoyait 0 si les chaînes sont égales, -1 si la première se classe avant l’autre, 1 si la première se classe après l’autre, est supprimé en python3. Voir la recette pour une définition de la fonction cmp() .
x in y retourne True si la chaîne x est incluse d’un seul tenant dans y ; souvent utilisé dans une condition
Note : python considère logiquement que toute chaîne, même vide, contient la chaîne vide :
>>> "" in "spam" ; "" in "" True True
Tri de lettres d’une chaîne
li =sorted(ch) retourne une liste triée des caractères utilisés dans la chaîne (plus d’informations ici)
>>> sorted("Monty Python") [' ', 'M', 'P', 'h', 'n', 'n', 'o', 'o', 't', 't', 'y', 'y']
Pour obtenir une chaîne, utiliser join() :
>>> "".join(sorted("Monty Python")) ' MPhnnoottyy'
reversed("abcdef") produit un objet <reversed object> on peut l’égrener avec for i in reversed("abcdef") : print(i)ou le recomposer en chaîne avec "".join() :
>>> "".join(reversed("Monty Python")) 'nohtyP ytnoM'
Il est possible en même temps de trier et d’inverser la chaîne :
>>> 'yyttoonnhPM '
En UNIX, et pour autant que la locale soit paramétrée en UTF-8, il est possible d’activer le tri «naturel» c’est-à-dire mélangeant majuscules et minuscules, lettres accentuées ou non (merci à Tyrtamos) :
>>> import locale >>> locale.setlocale(locale.LC_ALL, "") >>> "".join(sorted("côté", key=locale.strxfrm)) 'céôt'
Note : les méthodes li.sort() et li.reverse(), propres aux listes, ne s’appliquent pas aux chaînes.
ch ="abcdef" ; print(ch[::-1]) renverse également une chaîne : fedcba, sans passer par une liste
4.4 Encodages Rév. 2023.04.19
L’encodage par défaut du python3 est l’UTF-8, capable de gérer à peu près tous les caractères (1,2 Mo) de toutes les écritures du monde. Veuillez songer à sauvegarder vos scripts en UTF-8 et paramétrer la console en ce même mode.
Il est possible de modifier l’encodage par une directive à placer sur la première ligne du script :
# -*- coding: latin1 -*-
# -*- coding: cp1252 -*-
# -*- coding: latin9 -*-
…ou juste après la ligne rendant le script autoexécutable (en Unix) :
#! /usr/bin/python3 # -*- coding: ascii -*-
Note :
- il faut rendre la sauvegarde des scripts .py compatibles («sauvegarder sous» et préciser l’encodage) ainsi que la console («Définir le codage des caractères») si le script l’utilise pour ses sorties.
- -*- semble plus être une décoration qu’une nécessité, qui serait issue d’EMACS.
ASCII, ISO-8859, WIN1252
Du temps des premières communications électriques, 32 caractères de contrôle étaient utilisés à des fins diverses (invitation à transmettre, (in)disponibilité, shake-hand...). Très peu sont actuellement nécesaires (les protocoles TCP/IP s’en chargent), il ne reste pour coder les textes simples que quelques octets entre 0 et 31, comme l’octet 8 de retour à gauche, l’octet 9 de tabulation (\t), l’octet 10 de saut de ligne (\n), l’octet 13 de fin de paragraphe (\r) et l’octet 27 pour les séquences d’échappement, utiles en émulation de terminal VT··· et pour les imprimantes. Ces 32 premiers octets ont été intégrés au code ASCII, qui totalise 128 octets différents.
Dans le tableau suivant, le nombre \#NM formé par \xN_ (colonne de gauche) et _M (première ligne) représente le code «ASCII» en hexadécimal ; survoler un caractère affiche le même nombre en décimal : ord("!") =33 et chr(33) ="!"
La plage ASCII (les huit premières lignes) est commune à tous les encodages (latin1, cp1252, UTF-8…). Il suffit d’un octet pour le coder en n’importe quel encodage. Le premier caractère (32) est l’espace, le dernier (127) est normalement le caractère de suppression, mais n’affiche rien. Les six dernières lettres appartiennent à la norme ISO-8859-1 (latin1), et à peu de choses près, à la norme ISO-8859-15 (latin-9) qui introduit notamment les symboles €, Œ et œ. La bande grisée du milieu est le complément d’une trentaine de caractères spéciaux introduits par le jeu de caractère win1252 / cp1252, qui dans l’Unicode se retrouvent éparpillés plus loin.
_0 | _1 | _2 | _3 | _4 | _5 | _6 | _7 | _8 | _9 | _A | _B | _C | _D | _E | _F | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
\x0_ | ||||||||||||||||
\x1_ | ||||||||||||||||
\x2_ | ||||||||||||||||
\x3_ | ||||||||||||||||
\x4_ | ||||||||||||||||
\x5_ | ||||||||||||||||
\x6_ | ||||||||||||||||
\x7_ | ||||||||||||||||
\x8_ | ||||||||||||||||
\x9_ | ||||||||||||||||
\xA_ | ||||||||||||||||
\xB_ | ||||||||||||||||
\xC_ | ||||||||||||||||
\xD_ | ||||||||||||||||
\xE_ | ||||||||||||||||
\xF_ |
Les encodages latins
En français, l’utilisation du latin-1, un standard ISO-8859 ou du cp1252 est plus pratique que l’encodage ASCII (sans prendre plus de place en mémoire ou sur le disque) et plus économe que l’UTF-8, mais il est nécessaire de le préciser en tout début de script par l’une de ces lignes :
# -*- coding: latin1 -*- # -*- coding: latin9 -*- # -*- coding: cp1252 -*-
L’encodage latin1 (iso8859) ajoute les six dernières lignes du tableau ci-dessus. Un octet suffit pour coder chacun de ces caractères. L’octet \xA0 (160) est l’espace insécable ( en HTML), l’octet \xAD est la césure possible (­ en HTML).
173-
L’encodage latin9 (iso8859-15) modifie légèrement le latin1 : \xa4 – \xa6 164€ – \xa8 166Š – \xb4 168š – \xb8 180Ž – \xbc 184ž – \xbd 188Œ – \xbe 189œ, que le cp1252 a intégré dans ces deux lignes supplémentaires.
190Ÿ
L’encodage cp1252 utilise les deux lignes (en sombre dans le tableau) non utilisées par l’encodage latin1, voir cette page.
Notes :
- latin, latin1, latin-1, iso8859 et iso8859-1 sont équivalents
- latin9 et iso8859-15 sont équivalents, latin-9 n’existe pas
UTF-8 et Unicode
Les encodages «à l’octet» sont de plus en plus remplacés par l’UTF-8 qui permet l’utilisation de tous les caractères existant selon l’ordre Unicode, en utilisant d’un à quatre octets, mais ils n’apparaîtront que dans la mesure où les caractères sont présents sur une fonte chargée sur votre système.
Le module interne unicodedata permet d’obtenir des indications sur les caractères unicode :
>>> import unicodedata >>> print(chr(8212), unicodedata.name(chr(8212))) — EM DASH
Chaînes utf-8
L’encodage par défaut de python3 est l’UTF-8, qui permet l’utilisation des caractères Unicode.
Pour rappel, l’encodage par défaut ou spécifié au début du script doit correspondre au mode de sauvegarde du fichier de script et au codage de caractère de la console.
Pour donner un exemple plus évident qu’un idéogramme chinois, nous allons prendre le caractère é (qui existe dans les codages iso8859-1(5) et cp1252) au delà de l’octet 127 :
>>> "é" , len("é") , bytes("é", encoding="utf-8") ('é', 1, b'\xc3\xa9')
…ce qui montre que la chaîne é est considéré comme une chaîne d’un élément, mais composé de deux octets.
En python3, la chaîne n’affiche aucun caractère «composé», la chaîne n’étant pas de type UTF-8 :
>>> print("\xc3\xa9 \xc3 \xa9") é à ©
ord() renvoie le rang unicode du caractère :
>>> chr(233) ; ord("é") 'é' 233
chaine.encode(encodage) produit la chaîne d’octets (class 'bytes') à partir d’une chaîne unicode. 197 et 147 codent en deux octets le caractère unicode œ. Comparer :
>>> print("œuf") œuf >>> print("œuf".encode("cp1252")) b'\x9cuf' >>> print("œuf".encode("latin1")) b'\x9cuf' >>> print("œuf".encode("utf-8")) b'\xc5\x93uf'
ch est alors de la classe bytes.
ascii() code une chaîne non-ASCII (avec des lettres accentuées, caractères semi-graphiques, idéogrammes…) en chaîne uniquement composée de caractères ASCII (de 32 à 127) :
>>> ascii("pµə") "'p\\xb5\\u0259'"
- p littéral parce que son rang 'ascii/unicode' se situe entre 32 et 127
- \xb5 pour µ parce que son rang 'unicode' est entre 160 et 255
- \u0259 pour ə parce que son rang 'unicode' est au délà de 255
Note : les caractères entre 128 et 159 du cp1252 sont codés par leur équivalent unicode ; au delà de 255, voir cette page (1,2Mo).
4.5 Objets bytes et bytearray
Le codage par défaut des caractères en UTF-8 ne permet plus de considérer les chaînes comme une suite d’octets, les caractères étant codés d’un à quatre octets… (UTF-8), les caractères supérieurs à \xF0 (249 et supérieurs) n’étant jamais inclus dans un codage de chaîne UTF-8, contrairement aux encodages en octets comme iso8859 et cp1252. Avec le support UTF-8 (déjà en python2) a donc été créé les chaînes bytes, ne serait-ce que pour pouvoir sauvegarder des fichiers binaires pouvant contenir tous les octets de 0 à 255.
Les octets entre \x32 et \x127 s’écrivent avec les caractères ASCII correspondant, le reste est écrit en notation hexadécimale, soit deux chiffres hexadécimaux (de 1 à 9 et de a à f) préfixé par \x. Par exemple, 169 (16 *10 +9) se code \xa9 (a =10 ; b =11 ; c =12 ; d =13 ; e =14 ; f =15 ).
b"" définit un objet bytes en utilisant les caractères non accentués, ponctuation courante et opérateurs ou la notation hexadécimale à deux chiffres :
>>> b"w\x45\xff" b"wE\xff"
…où \x45 est rendu avec son équivalent ASCII E (économie d’écriture).
Il est à noter que si un objet 'bytes' ressemble à une chaîne, il s’agit en fait d’un tuple d’entiers ayant pour valeurs possibles de 0 à 255 :
>>> oc =b"ay\xff" >>> oc[1] ; type(oc[1]) 121 <class 'int'>
bytes() transforme une chaîne en objet bytes si l’on précise un encodage :
>>> bytes("là", encoding="utf-8") # trois octets en UTF-8 b'l\xc3\xa0' >>> bytes("là", encoding="latin-1") # deux octets en latin-1 b'l\xe0' >>> b =bytes("là", encoding="cp1252") ; type(b) ; type(b[1])) <class 'bytes'> <class 'int'>
Pour plus d’informations sur type(), voir Introspection.
D’autres types de données peuvent servir à créer un objet bytes :
>>> bytes([245, 23, 56, 255]) # les listes simples d’entiers entre 0 et 255 compris b'\xf5\x178\xff' >>> bytes((33, 64, 255)) # les tuples d’entiers entre 0 et 255 compris b'!@\xff' >>> bytes({3:"456", 33:"et"}) # les clés (entiers entre 0 et 255 compris) de dictionnaires (! vaut pour 33) b'\x03!'
Attention : Un entier seul génère initialise un objet 'bytes' à autant d’octets nuls :
>>> bytes(7) # b'\x00\x00\x00\x00\x00\x00\x00'
…ce qui peut servir à réserver un objet bytes d’une certain nombres d’octets. Mais comme il n’est pas possible de modifier un élément d’un objet bytes ; voyez plutôt l’objet bytearray.
Pour sauvegarder un tel objet, il faut utiliser un fichier binaire :
ch =bytes("déjà là", encoding="utf-8") with open("fichier.bin","wb") as fd : fd.write(ch)
Transformer un objet bytes en chaîne
Ajout le 2023.10.17
decode() convertit un objet bytes en chaîne, à condition de préciser son format d’encodage. Pour la variable bystr constitué d’un ensemble d’octets dont certains ont un rang supérieur entre 128 et 255 (codé avec \x..) :
>>> bystr =b"d\xc3\xa9j\xc3\xa0"
…l’encodage ascii émet une erreur, ne pouvant traiter les octets de rang de plus de 127 :
>>> bystr =b"d\xc3\xa9j\xc3\xa0" >>> bystr.decode("ascii") Traceback (most recent call last): File "<stdin>", line 1, in <module> UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 1: ordinal not in range(128)
La variable bystr représentant de l’UTF-8, il est bien transformé en chaîne.
>>> bystr =b"d\xc3\xa9j\xc3\xa0" >>> bystr.decode("utf-8") 'déjà'
Les encodages windows-1252, iso8859-1 ou iso8859-15 (dont chaque caractère est codé sur un byte) se débrouillent comme ils peuvent :
>>> bystr.decode("windows-1252") 'déjÃ\xa0'
Pour un objet bytes en jeu de caractères codés sur un octets :
>>> bystr2 =b"d\xe9j\xe0"
windows-1252, iso8859-1 ou iso8859-15 réagissent bien :
>>> bystr2.decode("iso8859-1") 'déjà'
…tandis que l’UTF-8 au codage sophistiqué n’en sortira probablement pas :
>>> bystr2.decode("utf-8") Traceback (most recent call last): File "<stdin>", line 1, in <module> UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe9 in position 1: invalid continuation byte
Objets bytearray
Un objet bytes se comporte comme un tuple ou une chaîne, il n’est pas possible de modifier un de ses éléments. Pour un objet dont on veut modifier les éléments comme une liste d’entiers entre 0 à 255, utiliser un objet bytearray :
>>> a =bytearray([0, 33, 53]) ; a bytearray(b'\x00!5') >>> a[2] =54 ; a bytearray(b'\x00!6')
…variable qui pourra être sauvegardée dans un fichier binaire :
#! /usr/bin/python3 b =bytearray([34, 98, 64, 75, 85, 33]) with open("fichier.bin", "wb") as fd : fd.write(b)
Il est possible d’initialiser un objet bytearray de n octets vides avec bytearray(n) :
>>> bytearray(7) bytearray(b'\x00\x00\x00\x00\x00\x00\x00')
5. Collections
Pour les généralités sur les variables, voir la section variables et assignations.
Python connaît plusieurs types de collections (chaînes vues plus haut, tuples, listes, dictionnaires et ensembles…) c’est-à-dire de variables composées d’éléments qui peuvent être divers (nombres, chaînes ou parfois d’autres collections). Les chaînes sont considérées comme des tuples simples de caractères.
5.1 Tuples (,)
Pour les généralités sur les variables, voir la section variables et assignations.
Un tuple (parfois traduit en français par n-uple) est une séquence non modifiable, une collection ordonnée d’éléments qui peuvent être des chaînes, nombres, listes, tuples, ensembles…
tup =(1, 2, "trois", [4, 5], 6) ou tup =1, 2, "trois", [4, 5], 6 définit le tuple tup. 1 est au décalage 0, 2 est au décalage 1, "trois" au décalage 2, le sous-tuple (4, 5) au décalage 3…
tup =(5,) ou tup =5, une virgule permet de n’assigner qu’un élément à un tuple : tup =(5) équivaut à tup =5, variable entière
tup =1, 2, 3, (4, 5), 6 les parenthèses d’un éventuel sous-tuple sont toujours nécessaires
tup[3][1] (notez les crochets !) renvoie 5, le second élément du sous-tuple à la position 4 (la numérotation des positions commence à 0)
tuple() transforme une liste, chaîne, ensemble ou rassemble les clés d’un dictionnaire en tuple
type(var) renvoie <class 'tuple'> si l’expression est un tuple, voir Introspection.
len(tup) renvoie le nombre d’éléments d’un tuple. len((1, (2, 3), 4)) ne contient que 3 éléments
Un tuple ne supporte pas la modification de valeur d’un de ses éléments ni la suppression d’un des éléments (utilisez des listes pour cette fonctionnalité) :
tup =3, 5, 6 ; tup[2] =45 renvoie TypeError: object doesn’t support item assignment :
tup =3, 5, 6 ; del tup[1] renvoie TypeError: 'tuple' object does not support item deletion
Un tuple est donc figé et ne peut être modifié que par une rédéfinition complète ou par un ajout d’élément(s). Pour une variable tup :
>>> tup =("a","b") >>> tup +=("c",) >>> tup =("d",) +tup >>> tup ('d', 'a', 'b', 'c')
tup =() définit un tuple vide, extensible avec +=
* permet de multiplier un tuple par un entier
>>> tup =(0, "", None, False) >>> tup *3 (0, '', None, False, 0, '', None, False, 0, '', None, False)
*= assigne directement le résultat à la variable
tup =("a", "b", "c", "d") ; tuple(reversed(tup)) renverse un tuple : ('d', 'c', 'b', 'a')
"".join(("a","b","cd","ef")) joint le tuple de caractères/chaînes en une chaine : "abcdef"
Python considère une chaîne comme un tuple de caractères :
>>> li =[1,"eggs"] ; print(li[1][3]) 's'
Les chaînes étant considérés comme des tuples de caractères, il est possible de séparer les caractères par un (ou plusieurs) caractère(s) avec join() sans devoir auparavant exploser la chaîne avec split() :
>>> "-".join("abcd") 'a-b-c-d'
5.2 Listes [,]
Pour les généralités sur les variables, voir la section variables et assignations.
Une liste est une collection ordonnée, une séquence modifiable d’objets divers : nombres, chaînes, listes, tuples, dictionnaires, ensembles… Chaque élément est classé selon un index numérique et peut être librement redéfini (une chaîne peut être remplacée par un nombre ou une collection).
Les listes ne sont pas des tableaux, mais il est possible de les utiliser comme tels. Pour les véritables tableaux, voir le module externe numpy.
li =[2, "oh", 9, 47] définit la liste li, composée de 4 valeurs : le nombre 2 est au décalage 0, la chaîne "oh" est au décalage 1, 9 au décalage au 2, etc.
len([2, "oh", (6, 3), 9, 47]) renvoie le nombre d’éléments de la liste, en l’occurrence 5 éléments, dont le tuple (6, 3).
list() transforme en liste une chaîne, un tuple, un ensemble, ou rassemble les clés d’un dictionnaire
>>> list("spam") ['s', 'p', 'a', 'm']
li =[] initialise une liste vide, peut être nécessaire lorsqu’elle doit partir de rien, voir exemple
li[0] désigne le premier élément de la liste, li[1] le deuxième…
li[-1] désigne le dernier élément de la liste, li[-2] l’avant-dernier…
li[1] =43 assigne la valeur 43 à la deuxième place de la liste li
li =[1, 9, [7, 4, 5]] une liste peut en contenir une autre parmi ses éléments
li[2][0] désigne le premier élément de la sous-liste à la troisième position : 7
liste =liste[debut:fin] sélectionne une plage de liste à partir de la position debut(la toute première est 0) jusqu’à la position fin non comprise (la dernière est -1, l’avant-dernière -2…)
li[3:7] à partir de la quatrième position jusqu’à la huitième position non comprise
li[3:] à partir de la quatrième jusqu’à la dernière comprise
li[0:-1] exclut le dernier élément
li[:-2] à partir de la première position jusqu’à l’avant-dernière non comprise
li[m:n:i] renvoie l’élément au décalage m, puis tous les i éléments jusqu’à n-1
li[:0] =["a", "b", "c"] ajoute des éléments en début de liste
li[2:2] =["a", "b", "c"] insère des éléments entre les positions 1 et 2
li[m:n] =["a", "b", "c"] supprime les positions m à n-1 et insère des éléments entre les positions m et n
del li[m:n] supprime les positions de m à n-1,
slice()
slice(start, end, step) crée un objet 'slice' qui contient le paramétrage d’un découpage selon les attributs start, stop et step, soit les trois arguments possibles (stop seul, start et stop, ou les trois ensemble, les attributs non définis renvoient None) :
>>> i =slice(1, 7) >>> print(i.start, i.stop, i.step) 1 7 None
Exemple d’utilisation :
>>> sl =slice(1, 13, 2) >>> print("Monthy Python"[sl]) otyPto
Ajouter des éléments
+ prolonge une liste par une autre liste
>>> li =["Spam"] ; li +["Eggs"] ["Spam", 3] >>> li =li +"Eggs" TypeError: can only concatenate list (not "str") to list
Pour ajouter un ou des élément·s à une liste, il faut l(es) entourer de [] :
>>> li =[2, 3, 5, 7] >>> li +=[11, 13] ; li [2, 3, 5, 7, 11, 13]
Pour ajouter une sous-liste, il faut l’entourer deux fois : [[]] :
>>> li =[2, 3, 5, 7] >>> li +=[[11, 13]] ; li [2, 3, 5, 7, [11, 13]]
* permet de multiplier une liste
>>> ["Spam"] *3 ['Spam','Spam','Spam']
Alias
Attention à l’assignation simple d’une liste à une autre, car cette assignation est en fait une identité :
>>> liste =[1, 2, 3] ; autre =liste >>> autre +=[7] ; print(liste, autre) [1, 2, 3, 7] [1, 2, 3, 7]
autre n’est donc pas une autre liste comportant les mêmes éléments, mais un alias. On utilise list() ou b =a[:] pour copier entièrement une liste dans une autre :
>>> originale =[1, 2, 3] ; copie =list(originale) >>> copie +=[7] ; >>> print(originale, copie) [1, 2, 3] [1, 2, 3, 7]
Il n’est pas possible de créer un tableau de dimension deux en multipliant une liste, chaque modification sur une des sous-listes se répercutant sur les autres :
>>> echiquier =[[0] *8] *8 >>> echiquier[3][3] =7 >>> echiquier [[0, 0, 0, 7, 0, 0, 0, 0], [0, 0, 0, 7, 0, 0, 0, 0], [0, 0, 0, 7, 0, 0, 0, 0], [0, 0, 0, 7, 0, 0, 0, 0], [0, 0, 0, 7, 0, 0, 0, 0], [0, 0, 0, 7, 0, 0, 0, 0], [0, 0, 0, 7, 0, 0, 0, 0], [0, 0, 0, 7, 0, 0, 0, 0]]
Il existe
Une étude plus approfondie du problème sur cette page, avec une recette pour initialiser un tableau à plusieurs dimensions.
Pour générer des listes, voir 5.7 Génération de collections.
Récursion
Il est possible d’utiliser la boucle for pour accéder à chaque élément d’une liste.
liste = [0, 1, 2, 3, 4, 5, 6, 7] for i in liste : print(i)
Il est possible d’obtenir la récursion inverse grâce à liste[::-1] :
liste = [0, 1, 2, 3, 4, 5, 6, 7] for i in liste[::-1] : print(i)
C’est très pratique si l’on doit éliminer certain éléments sans perturber le rang des éléments encore à traiter ni devoir résoudre le problème de la diminution de la longueur de la liste :
liste = [0, 1, 0, 3, 4, 0, 6, 0] for i in liste[::-1] : if liste[i] ==0 : liste.pop(i) print(liste)
Pour pouvoir modifier la valeur des éléments d’une liste, il faut y accéder par l’index :
liste = [1, 5, 2, 9, 0, 6, 3] for i in range(len(liste)) : if liste[i] < 4 : liste[i] =liste[i] *100 print(liste)
Liste et doublons
Pour enlever les doublons d’une liste, il est possible de passer par un ensemble, qui est une collection (non ordonnée) d’objets uniques
>>> print(list(set([1,1,3,4,5,5,5]))) [1,3,4,5]
On risque de perdre l’ordre, les ensembles n’étant pas une collection ordonnée. Cela ne marche pas pour toutes les listes, comme par exemple celles qui contiennent des sous-listes ou d’autres types de collections : voir cette recette.
5.3 Méthodes pour les listes
li.sort() et li.reverse() sont présentés dans la section suivante.
n =li.count(elem) compte le nombre d’occurrences de la valeur elem dans la liste
n =li.count(elem, n) …à partir de l’élément à la position n
n =li.index(elem) retourne le décalage de la première occurrence de elem dans la liste (ValueError si n’existe pas)
n =li.index(elem, n) retourne le décalage de la première occurrence de elem à partie du décalage n (ValueError si moins de n +1 cellules dans li)
li2 =li.copy() crée une nouvelle liste li2 à partir de li. Les deux listes sont indépendantes, alors que li2 =li crée un alias de la liste li
Modifie directement la liste :
li.clear() vide la liste de tous ses éléments, le résultat est la liste vide [].
li.pop() renvoie le dernier élément et le supprime de la liste (IndexError si la liste est vide)
li.pop(i) retourne l’élément au décalage i, le supprime de la liste et ramène tous les élément suivants (IndexError si la liste est vide)
li.remove(x) supprime la première occurrence de la valeur x dans la liste. Provoque une erreur si la valeur x n’existe pas ! Solution :
if x in li : li.remove(x)
li.insert(i, valeur) insère une valeur au décalage i en repoussant les éléments suivants, ou en l’ajoutant à la fin si le décalage équivaut au nombre d’éléments de la liste
li.append(elem) ajoute un élément (nombre, chaîne, liste, tuple, dictionnaire) à la fin d’une liste ; équivaut à liste +=[elem]
li.extend(seq) étend une liste avec chaque élément d’une séquence : les caractères d’une chaîne, les élément d’une liste ou d’un tuple, ou chaque clé d’un dictionnaire. Pour li =[1,2,3,4] et ch ="abc" :
>>> li.append(ch) [1, 2, 3, 4, 'abc'] >>> li.extend(ch) [1, 2, 3, 4, 'a', 'b', 'c']
Entre listes et chaînes :
li =chaine.split() découpe une chaîne (à chaque espace) et place les éléments dans une liste
li =chaine.split(ch) découpe une chaîne (à chaque sous-chaîne) et place les éléments dans une liste
chaine =" ".join(liste) assemble dans une chaîne les éléments d’une liste de chaînes, avec l’espace (ou toute autre caractère défini) comme séparateur.
5.4 Tris de liste Rév. 2023.04.26
Le tri des listes mérite une section particulière du fait qu’une fonction et une méthode coexistent, ainsi que des variantes impliquant des méthodes de modules.
tr =sorted(li) renvoie une liste triée ; la liste de départ ne l’est pas
>>>li =[4,2,1,3] ; tr =sorted(li) >>> print(li, tr) [4, 2, 1, 3] [1, 2, 3, 4]
Notes :
- un tri n’est possible que si les éléments sont de même type (nombres, chaînes, listes…)
- les dictionnaires n’étant pas comparables (< et > ), ils ne peuvent être pris comme éléments d’une liste à trier
- dans les ensembles set(), les comparateurs < et > signifient strictement inclus ou contenant strictement, et renvoient False dans tous les autres cas, ce qui ne garantit un tri que s’il s’agit d’une série d’ensembles emboîtés.
Le module interne locale possède une méthode pour un tri «naturel» (annuaire téléphonique) qui mélange majuscules et minuscules, lettres accentuées ou non et ne tenant pas compte de la ponctuation, guillemets ou espaces (merci à Tyrtamos) :
>>> import locale >>> locale.setlocale(locale.LC_ALL, "") >>> sorted(["C", "e", "â", "sa", "s b" ], key=locale.strxfrm) ['â', 'C', 'e', 'sa', 's b']
…à comparer avec :
>>> sorted(["C", "e", "â", "sa", "s b" ]) ['C', 'e', 's b', 'sa', 'â']
li.sort() trie une liste exprimée sous forme de variable (sans devoir récupérer le résultat dans une variable)
Le tri est celui de l’ordre des caractères : en gros la ponctuation et les opérateurs, les chiffres, les majuscules, les minuscules et puis les lettres accentuées
Il est possible d’utiliser le module icu (à installer), voir la page des modules.
li.reverse() inverse une liste exprimée sous forme de variable, la variable elle-même est modifiée
lis2 =li[::-1] copie une liste inversée
li.sort(reverse=True) combine les deux précédents.
Pour rendre le tri insensible à la casse (pas de distinction entre majuscules et minuscules), utiliser key=str.lower :
>>> li =["a", "A", "B", "b"] ; li.sort(key=str.lower) ['a', 'A', 'B', 'b']
En python3, il n’est plus possible de personnaliser le mode de comparaison avec sorted(var, cmp =lambda x, y : … ou une fonction renvoyant -1, 0 ou 1. Le paramètre cmp est supprimé ainsi que la fonction cmp().
Tri selon les valeurs d’une «colonne» en particulier
Le comportement de python fait que les listes qui comportent des sous-listes («rangées») sont triées en tenant compte de chaque premier élément des sous-listes (première «colonne»). En cas d’égalité, les seconds éléments sont évalués, etc. :
>>> li=[[2, "b"], [1, "b"], [2, "a"], [1, "a"]] >>> sorted(li) [[1, 'a'], [1, 'b'], [2, 'a'], [2, 'b']]
Si le module operator est importé, li.sort(key =operator.itemgetter(n)) permet de définir la colonne (n) sur laquelle la liste doit être triée.
>>> import operator >>> li=[[2, "b"], [1, "b"], [2, "a"], [1, "a"]] >>> sorted(li, key=operator.itemgetter(0)) [[1, 'b'], [1, 'a'], [2, 'b'], [2, 'a']]
Contrairement au premier exemple, python ne s’est pas préoccupé de la seconde colonne : il faut soi-même préciser les colonnes à soumettre au tri :
>>> li=[[2, "b"], [1, "b"], [2, "a"], [1, "a"]] >>> sorted(li, key=operator.itemgetter(1, 0)) [[1, 'a'], [2, 'a'], [1, 'b'], [2, 'b']]
Notes :
- la première colonne est au décalage 0, itemgetter(1) réalise donc le tri sur le deuxième élément de chaque «rangée» (sous-liste) de la liste
- si une sous-liste n’est pas assez longue et ne contient pas l’élément sur lequel itemgetter() réalise son tri, l’exception list index out of range interrompt le script
- rappels :
- les colonnes doivent comporter des éléments de même type
- les dictionnaires ne supportent pas la comparaison et ne peuvent pas être les éléments d’une liste à trier
- deux ensembles sont soit égaux, soit distincts, mais < et > renvoient toujours False, rendant le tri impossible (sans avertissement ni erreur)
5.5 Dictionnaires { : , }
Pour les généralités sur les variables, voir la section variables et assignations.
Pour un dictionnaire qui conserve la mémoire de l’ordre d’entrée de ses éléments : OrderedDict.
Un dictionnaire est une collection modifiable non ordonnée d’objets accessibles par des clés.
>>> nl ={1:"ek", 2:"do", 3:"tin", 4:"char"} ; nl[1] 'ek'
La clé est une valeur ou une variable contenant une telle valeur.
>>> nl ={1:"één", 2:"twee", 3:"drie", 4:"vier", 5:"vijf"} >>> nl[1] ; d =2 ; nl[d] ; nl[9**.5] ; nl[4 +0j] ; nl[round(5.123)] 'één' 'twee' 'drie' 'vier' 'vijf'
Attention : un dictionnaire n’est pas une liste : on accède à la valeur d’un dictionnaire par sa clé, non par son décalage :
>>> hindi ={"un": "ek", "deux": "do", "trois": "tin", "quatre": "char", "cinq": "panch"} >>> print(hindi["cinq"]) # la clé est ici la chaîne "cinq" >>> hindi[3] KeyError: 3
Par contre, une valeur peut être une liste, à l’intérieur de laquelle un décalage peut servir :
>>> dic ={"spam": [1, 2, "eggs", 4]} >>> dic["spam"][2] # affiche le décalage [2] de la liste dont la clé est "spam" 'eggs'
Notez bien:
- les clés sont limitées aux nombres (même «réels» ou complexes), des chaînes (même vide) ou des tuples ou la valeur d’une variable : c’est cette valeur au moment de la définition du dictionnaire qui est retenue
- il n’y a pas de restriction pour les valeurs, qui peuvent également être des listes, ensembles ou dictionnaires :
mondic ={"":[0, 1], "a":97, (1,2,3):{4:2}}
dico ={} et dico =dict() initialisent un dictionnaire
type(dico) renvoie <class 'dict'>, voir Introspection.
del dico[cle] enlève le couple cle:valeur d’un dictionnaire
del dico[1:3] retourne TypeError: unhashable type : pas de référence par plage
Attention : des variables peuvent représenter des valeurs pour les clés. Mais dans l’exemple suivant, même si a et b sont différents dans la forme id(a) !=id(b), ils sont égaux au point de ne pouvoir être les clés de deux valeurs différentes : la variable b remplace la valeur de la variable a utilisée comme clé ; appeler dic[b] équivaut à appeler dic[a] puisque leur valeur commune est 2.
>>> a =2 +0j ; b =2 >>> id(a) ==id(b) False >>> dic ={a:25, b:31} >>> dic {(2 +0j): 31} >>> dic[a] ; dic[b] 31 31
Note : une liste peut contenir un ou plusieurs dictionnaire·s non nommé·s, accessible·s par leur position dans la liste :
>>> li =[{}, {}, {}] >>> li[0]["a"] =3 ; li[0]["b"] =4 ; li[2]["c"] =5 >>> li [{'a': 3, 'b': 4}, {}, {'c': 5}]
Attention : de la même manière que pour les listes, dic1 =dic2 n’assigne pas seulement les valeurs de dic2 à dic1, mais fait de dic1 un alias de dic2 :
>>> a ={"zwin":3} >>> b =a >>> b["panne"] =4 >>> a ; b {'zwin': 3, 'panne': 4} {'zwin': 3, 'panne': 4}
Pour seulement transférer les valeurs de a dans b et se permettre de les faire évoluer différemment, utiliser b =dict(a)
5.6 Méthodes pour dictionnaires
L’appel à une clé inexistante dans un dictionnaire avec dico[cle] génère une erreur. Il est prudent de prévoir une condition contenant la méthode suivante :
dico.get(cle) renvoie None si la clé n’est pas définie
dico.get(cle,"clé inexistante") retourne la chaîne prévue si la clé n’est pas définie
Il est plus simple d’utiliser le mot-clé in pour ce contrôle :
if "a" in dico : val =dico["a"]
dico[cle] =valeur ajoute une valeur associée à une nouvelle clé au dictionnaire dico ou modifie la valeur associée à cette clé
dico.pop(clé) renvoie et supprime la valeur définie par une clé (ainsi que la clé)
dico.popitem() renvoie une paire (clé-valeur) et la supprime du dictionnaire. Il faut se rappeler qu’un dictionnaire n’est pas une collection ordonnée, et la méthode popitem() ne renverra peut-être pas la dernière paire clé:item rentrée.
dico.clear() supprime tous les éléments du dictionnaire dico
dic1.update(dic2) ajoute les occurrences du dictionnaire dic2 au dictionnaire dic1, en écrasant les valeurs du dictionnaire dic1 par celles de dic2 pour les clés communes aux deux dictionnaires. L’intuitif dic1 +=dic2 n’est pas permis !
dic2 =dic1 donne un deuxième nom au même objet : modifier l’un modifie l’autre
dic2 =dic1.copy() copie un dictionnaire dans un autre, les modifications de l’un n’affectent pas l’autre
dico.keys() crée un objet itérable des clés du dictionnaire dico (<class 'dict_keys'>)
dico.values() crée un objet itérable des valeurs du dictionnaire dico (<class 'dict_values'>)
dico.items() crée un objet itérable contenant les tuples clés/valeurs du dictionnaire dico (<class 'dict_items'>)
À l’inverse, il est possible de créer un dictionnaire à partir d’une collection (liste, tuple ou ensemble) de paires (listes ou tuples) :
>>> dict({(2, "1"), ("8", 4)}) {2: '1', '8': 4}
Il est possible d’assembler deux listes (ou tuples) de clés et de valeurs en un dictionnaire à l’aide de zip() :
>>> cles =("a", "b", "c") ; valeurs =[1, 2, 3] >>> dict(zip(cles, valeurs)) {'a': 1, 'b': 2, 'c': 3}
Pour obtenir une liste des clés, il suffit d’utiliser list() : list(dico.keys()), mais ce n’est pas souvent nécessaire, puisque l’objet est itérable.
dico ={"a":1, "b":2, "c":3}
for i in dico.keys() :
print(i)
sum(mondico) renvoie la somme des clés du dictionnaire mondico, une erreur si elle ne sont pas toutes numériques
sum(mondico, n) ajoute n à la somme des clés de mondico
Pour la sommation des valeurs (toutes numériques) d’un dictionnaire : sum(mondico.values())
Pour inverser les clés et les valeurs d’un dictionnaire, il est possible d’utiliser une définition en compréhension. Attention toutefois, deux valeurs égales du dictionnaire de départ ne feront plus qu’une clé du dictionnaire d’arrivée, une clé du premier dictionnaire écrasera dans ce cas l’autre en tant que valeur du dictionnaire d’arrivée :
>>> dico ={"a":1 , "b":2, "c":3, "d":2} >>> inv ={i:val for val, i in dico.items()} >>> inv {1: 'a', 2: 'd', 3: 'c'}
ou, parce que les dictionnaires ne sont pas ordonnés :
{1: 'a', 2: 'b', 3: 'c'}
Quelques remarques sur ce procédé :
- le premier dictionnaire ne peut contenir de collections, puisqu’une collection ne peut servir de clé dans le dictionnaire inversé (TypeError: unhashable type: 'dict' ou 'list' )
- en cas de plusieurs valeurs égales dans le premier dictionnaire, toute valeur répétée du dictionnaire de départ imposera sa clé comme valeur au dictionnaire «inversé»
- cela paraît un peu magique, mais cela fonctionne parce que dico.items() renvoie un objet itérable (class 'dict_items') des paires (clé, valeur) du dictionnaire dico
Trier un dictionnaire génère une liste des clés :
>>> sorted({"a":3, "c":7}) ['a', 'c']
La fonction reversed() appliquée sur un dictionnaire génère un objet itérable contenant les clés du dictionnaire :
reversed({"a":3, "c":7}) <dict_reversekeyiterator object at 0x7f8370ac7950>
min(dico) et max(dico) retourne la plus petite ou la plus grande clé d’un dictionnaire pour autant que les clés soient de même classe / type.
Pour transformer un dictionnaire en une liste de paires clés/valeurs (par exemple pour pouvoir les trier), on utilise zip() :
dico ={"b":1, "a":4, "d":3, "c":2} list(sorted(zip(dico.keys(), dico.values()))) [('a', 4), ('b', 1), ('c', 2), ('d', 3)]
5.7 Ensembles {,}
Pour les généralités sur les variables, voir la section variables et assignations.
Python propose depuis sa version 2.3 une classe d’«ensembles», qui sont des collections non ordonnées d’éléments uniques.
>>> set(["x", 3, 2, 2, 4]) {3, 2, 'x', 4}
Les doublons sont automatiquement éliminés, ce qui peut être utile pour les listes.
Les ensembles peuvent être définis à partir d’une liste, d’une chaîne, d’un tuple ou d’un dictionnaire ; dans ce dernier cas, seules les clés, qui sont uniques, sont considérées.
>>> set("eggs") ; set({"a":3,"b":6}) set(['s', 'e', 'g']) set(['a', 'b'])
En Python, une collection ne peut pas être élément d’un ensemble :
>>> set([1,{2,3}]) TypeError: unhashable type: 'set'
Il existe une exception pour les chaînes : les caractères d’une chaîne seule seront autant d’éléments (hormis les doublons) de l’ensemble, mais plusieurs chaînes seront autant d’éléments (hormis les doublons) :
>>> set("eggs") ; set(["spam","eggs"]) {'e', 'g', 's'} {'eggs', 'spam'}
len(s) renvoie le nombre d’éléments de l’ensemble
x in s retourne True si l’élément x appartient à s (condition permettant d’éviter KeyError).
Méthodes pour ensembles
s.clear() vide l’ensemble
nv =s.copy() copie un ensemble dans un autre. nv =s ne fait que créer un alias, c’est-à-dire un autre nom pour le même objet.
s.add(x) ajoute l’élément x s’il ne s’y trouve pas encore
s.discard(x) enlève l’éventuel élément x de s
s.remove(x) enlève l’élément x, KeyError générée si x n’appartient pas à s.
s.pop() enlève un élément au hasard, KeyError générée si l’ensemble est vide
a.issubset(b) (ou a <= b) retourne True si a est contenu dans (ou est égal à) b ; (a < b si a est un sous-ensemble strict de b)
a.issuperset(b) (ou a >= b) retourne True si a contient (ou est égal à) b ; (a > b si a contient strictement b)
a.union(b) (ou a|b) retourne les éléments de a ou b
a.update(b) (ou a|=b) ajoute les éléments de b à a
a.intersection(b) (ou a&b) renvoie les éléments communs à deux ensembles
a.intersection_update(b) (ou a&=b) ne conserve à a que les éléments communs aux deux ensembles
a.difference(b) (ou a-b) renvoie les éléments de a n’appartenant pas à b
a.difference_update(b) (ou a-=b) retire à a tous les éléments communs avec b
a.symmetric_difference(b) (ou a^b) retourne les éléments non commun à a et b
a.symmetric_difference_update(b) (ou a^=b) retire à a tous les élément communs avec b
Note : la méthode + n’est pas définie pour les ensembles. Additionner les éléments des deux ensembles en ne comptant qu’une fois les éventuels éléments communs se fait avec la méthode union() ou l’opérateur |
Trier un ensemble génère une liste :
>>> ens ={5, 1, 4, 6, 2} ; li =sorted(ens) ; print(li) [1, 2, 4, 5, 6]
La fonction reversed() ne peut s’appliquer aux ensembles (TypeError: 'set' object is not reversible).
frozenset() permet la définition d’ensemble fixes, et ne permet donc pas les modifications par les méthodes clear(), add(), pop(), discard(), remove() ni update()
5.8 Génération de collections
Révision 2023.01.11
range()
range(debut, fin, pas) crée un objet 'range', un générateur, collection de type spécial générée à partir d’une progression arithmétique finie s’arrêtant juste avant le nombre fin. Il est possible de parcourir cette collection :
>>>for i in range(3) : … print(i) … 0 1 2
Un seul nombre signifie que l’objet commence à zéro et finit à n -1. Il est possible de transformer l’objet 'range' en liste :
>>> list(range(10)) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Deux nombres en paramètres sont le début et la fin -1 de la série
>>> list(range(5, 10)) [5, 6, 7, 8, 9]
Un troisième nombre est le pas de la progression :
>>> list(range(3, 27, 4)) [3, 7, 11, 15, 19, 23]
Une progression négative est possible , avec un pas négatif et un premier nombre plus grand que le second (pour arriver à 0, il faut «viser» -1) :
>>> list(range(10, 0, -1)) [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
La fonction range() est un générateur, qui ne produit le nombre que quand il faut, pour économiser de la mémoire et du temps.
Note : En python2, range() renvoyait une liste, ce qui pouvait prendre du temps avant qu’elle ne soit accessible (voir point suivant). xrange() a ensuite été implémenté en tant que générateur, collection dont les éléments ne sont créés qu’à la demande, ce qui économise place en mémoire et temps. Avec python3, range() a repris les caractéristiques de xrange(), qui n’existe plus.
Générateur
Un générateur est une collection dont les éléments sont produits à la demande. Son type est generator
>>> gen =(x **3 for x in range(10000000)) >>> type(gen) <class 'generator'>
Contrairement à une liste définie en compréhension, un générateur ne produit pas d'avance tous ses éléments. Comparez en terme de temps et d’espace mémoire libre (possible si vous disposez de 8Go) :
>>> gen =range(100000000) >>> li =list(range(100000000)) >>> del(li)
range() est quasi instantané et ne consomme pas de mémoire, au contraire de list(range()) (3.7Go). del(li) supprime la variable et libère la mémoire du système.
Un générateur peut être parcouru par une boucle for :
>>> for i in (x **3 for x in range(4)) : print(i) … 0 1 8 27
Avec yield remplaçant return, il est possible de créer une fonction qui a le même comportement qu’un générateur.
Collections en compréhension
Pour d’autres type de suites, on utilise les collections en compréhension, qui sont des générateurs transformés en listes, dictionnaires ou ensembles.
Pour une liste ([]) de carrés (x **2) des valeurs (x) variant de 0 à 9 (range(10)) :
>>> [x **2 for x in range(10) if x %2 ==1] [1, 9, 25, 49, 81]
Note : comme les variables locales définies à l’intérieur d’une fonction, une variable utilisée dans une définition en compréhension n’interfère pas avec une éventuelle variable globale de même nom :
>>> x =43 >>> a =[x for x in range(1, 11)] >>> a ; x [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 43
Définir un tuple en compréhension aboutit à un générateur, qu’il est possible de parcourir :
>>> gen =(x for x in "Ni!") ; gen <generator object <genexpr> at 0x7f91fa2da560> >>> for i in gen : print(i) N i !
Pour obtenir un tuple généré par cette méthode, il faut transformer le générateur avec tuple() :
>>> tup =tuple((x **2 for x in range(5))) ; tup (0, 1, 4, 9, 16)
Il est possible de générer des dictionnaires en compréhension. La syntaxe change pour prendre en compte la définition en couple clé:valeur :
>>> { x:x **2 for x in range(11, 20) } {11: 121, 12: 144, 13: 169, 14: 196, 15: 225, 16: 256, 17: 289, 18: 324, 19: 361} >>> cubes ={ x:x **3 for x in range(9) } >>> cubes[3] 27
Autre exemple :
>>> {x : chr(96 +x) for x in range(1,6)} {1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e'}
Il est possible de générer des ensembles set(), qui utilise également les {accolades}, supprime les doublons, et perturbe éventuellement l’ordre :
>>> { x**2 for x in range(7) for y in range(8) } {0, 1, 4, 36, 9, 16, 25}
Pour générer une chaîne, il faut joindre avec join() les résultats transformés (str) en chaînes (", " sépare les chiffres) :
>>> ", ".join(str(x **3) for x in range(10)) '0, 1, 8, 27, 64, 125, 216, 343, 512, 729'
Autre exemple, utilisant l’ordre «ASCII» des lettres de A à Z :
>>> ", ".join(chr(x) for x in range(65, 91)) 'A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z'
Il est possible d’imbriquer des boucles :
>>> [x *y for x in range(1, 3) for y in range(7)] [0, 1, 2, 3, 4, 5, 6, 0, 2, 4, 6, 8, 10, 12]
Cette liste est composée des résultats de la multiplication de x et y, x vaut 1, puis 2, pendant que y vaut successivement les valeurs de 0 à 6. Il en possible de filtrer les résultats par une condition (les multiples de 3 sont ici éliminés) :
>>> suite =[x *y for x in range(5) for y in range(7) if x *y %3 != 0]
…résume le bloc :
suite =[] for x in range(5) : for y in range(7) : if x *y %3 !=0 : suite +=[x *y]
Réponse :
[1, 2, 4, 5, 2, 4, 8, 10, 4, 8, 16, 20]
Cette imbrication et ce filtre (sans les : des boucles) peuvent s’appliquer aux autres collections, par exemple pour générer un ensemble, qui supprime les doublons et ne respecte plus l’ordre :
>>> { x *y for x in range(5) for y in range(7) if x *y %3 != 0 } {1, 2, 4, 5, 8, 10, 16, 20}
5.9 Fonctions pour collections
sorted() : voir chaîne, liste, dictionnaire, tuple, ensemble.
Dénombrement avec len()
len() renvoie le nombre d’éléments d’une collection (tuple, liste ou ensemble), le nombre de doublets clé:valeur d’un dictionnaire ou le nombre de caractères d’une chaîne.
Sommation avec sum()
sum([1, 2, 3, 4]) renvoie la somme des éléments contenus dans une collection seulement composée de nombres
sum([1, 2, 3, 4], 33) renvoie 43, un nombre initial pouvant être ajouté
Dans l’exemple suivant, la sous-liste [2, 6] ne peut pas être ajoutée aux élément 3 et 5.
>>> sum([3, [2,6], 5]) TypeError: unsupported operand type(s) for +: 'int' and 'list'
Pour les dictionnaires, la sommation porte sur les clés. Pour qu’elle porte sur les valeurs : sum(mondico.values())
Comparaisons
La comparaison entre collections se passent différemment selon le type de collections. Pour deux listes, c’est le premier élément qui est considéré, puis le deuxième, etc., ensuite la longueur si tous les éléments de l’une correspond à tous les premiers de l’autre :
>>> [1,2] < [2,1,3] True >>> [1,2,3,4] > [1,3,2] True
Pour les ensembles, < signifie l’inclusion stricte, > la contenance stricte :
a > b retourne True si a contient b et comprend au moins un autre élément
a < b retourne True si a est contenu dans b, lequel comprend au moins un élément supplémentaire
>>> {2,1} < {1,3,2} True
La comparaison est interdite entre dictionnaires. Pour les ensembles, la réponse n’a de sens qu’en cas d’inclusion :
>>> {1, 3} < {1, 3, 2} True
x in y retourne True si x est un élément de y, sauf pour les chaînes où x in y vérifie que x est une plage de y.
À conditions que leurs éléments soient tous de même classe / type,
max(x) renvoie le plus grand élément d’une collection
min(x) renvoie le plus petit élément d’une collection
À condition que ce qui est comparé soit tous de même classe / type,
max(x, y) renvoie la collection la plus «grande»
min(x, y) renvoie la collection la plus «petite»
Attention :
- max() et min() ne fonctionnent pas avec les complexes
- max() et min() ne fonctionnent pas pour comparer deux dictionnaires, mais max(dico) permet de trouver la plus grande clé si elles sont toutes de même classe / type.
- pour deux ensembles, max() (min()) désigne celui qui inclut (est inclus dans) l’autre. En dehors de ce cas de figure, il n’y a pas de tri possible et c’est le premier des deux ensembles qui est retourné.
cmp(x, y) qui renvoyait -1 si x<y, +1 si x>y et 0 si x ==y est suprimée en python3. Voir la recette pour une définition de la fonction cmp(), à écrire soi-même.
Tester
any() retourne True si au moins un élément d’une collection est vrai, c’est-à-dire différent de 0, None, False, "", [], () ou {}.
>>> any([0, "", {}]) False
La liste suivante contient la chaîne "0", qui n’est pas vide.
>>> any(["0", "", {}]) True
all() retourne True si aucun des éléments n’est «faux» (voir any() ci-dessus) :
>>> all([3, "hey", [43]]) ; all(([], [2])) True
La liste suivante contient trois éléments non faux, à savoir 3, [3,"eggs",0] et "spam" (même si [3,"eggs",0] contient l’élément 0).
>>> all([3, [3, "eggs", 0], "spam"]) True
Attention :
all([]) retourne True parce qu’une liste vide ne contient aucun élément faux (0, "", [ ]…)
all([[]]) retourne False parce que la liste contient une liste vide
all([[0]]) retourne True parce que la liste contient une liste non vide
Énumérer
enumerate(coll) génère une objet enumerate formé des paires (numéro d’ordre, élément) à partir d’une collection ordonnée :
>>> list(enumerate("spam")) [(0, 's'), (1, 'p'), (2, 'a'), (3, 'm')] >>> list(enumerate((3, 1, 4))) [(0, 3), (1, 1), (2, 4)] >>> list(enumerate([0, 5, 3])) [(0, 0), (1, 5), (2, 3)]
Dans le cas d’un dictionnaire, ce sont les clés qui forment la seconde valeur de la paire ;
>>> list(enumerate({"a":1, "b":3, "d":9})) [(0, 'a'), (1, 'b'), (2, 'd')]
Il est possible d’énumérer un ensemble, même si ce n’est pas une séquence ; la signification du résultat est obscure :
>>> list(enumerate(set("eggs"))) [(0, 'g'), (1, 's'), (2, 'e')]
Il est possible d’ajouter un départ :
>>> list(enumerate([4,6,7], start =1000)) [(1000, 4), (1001, 6), (1002, 7)]
zip(coll_1, coll_2, …) renvoie un objet 'zip' contenant des tuples : le premier tuple rassemble tous les premiers éléments des collections (chaînes de caractères, tuples, listes, dictionnaires ou ensembles), le deuxième tuple tous les deuxièmes éléments, etc. :
zz =zip("abcde", (1, 2, 3, 4, 5, 6, 7), ["u", "v", "w", "x", "y", "z"]) print(zz) <zip object at 0x7fbb79557280> for z in zz : print(z)
rendra :
('a', 1, 'u') ('b', 2, 'v') ('c', 3, 'w') ('d', 4, 'x') ('e', 5, 'y')
Remarque : la liste des tuples est limitée à la longueur de la plus petite collection. Pour forcer le résultat à longueur de la plus longue collection, utiliser zip_longest() du module interne itertools (remplissage des éléments manquants avec None) :
>>> import itertools >>> a=("a","b","c") ; b=[1,2,3] ; c ="oeuf" >>> list(itertools.zip_longest(a,b,c)) [('a', 1, 'o'), ('b', 2, 'e'), ('c', 3, 'u'), (None, None, 'f')]
Fonctions indésirables
Guido Van Rossum se repent d’avoir laisser implémenter quelques fonctions, surtout parce qu’elles sont très contournables.
map(fonction,variable) applique une fonction à chaque élément d’une collection, en l’occurrence, les caractères d’une chaîne. Le résultat est un objet itérable :
>>> list(map(ord, "python")) [112, 121, 116, 104, 111, 110]
Cette fonction peut facilement être remplacée par une liste en compréhension :
[ord(x) for x in "python"]
filter(fonction, collection) renvoie un objet 'filter' contenant des éléments de d’une collection filtrés par une fonction censée recevoir une valeur et retournant True selon une condition sur la valeur reçue. Pour filtrer une chaîne en ne retenant que les lettre avec un code ASCII pair :
>>> def fct(x) : … if ord(x) %2 ==0 : return True … >>> "".join(filter(fct, "python")) 'pthn'
Il est possible de préciser la fonction filtrante avec lambda, dans un objet itérable :
>>> "".join(filter(lambda x : ord(x) %2 ==0, "python")) 'pthn'
Il est très facile de remplacer filter() et lambda par une collection en compréhension :
>>> "".join(x for x in "python" if ord(x) %2 ==0) 'pthn'
À condition que le module functools soit importé, reduce(fct, lst) réduit une séquence : liste, tuple, clés de dictionnaire, ensemble… par une fonction à deux arguments qui s’applique sur toute la séquence. Dans l’exemple, la fonction lambda définit une multiplication, qui sera successivement appliquée à tous les éléments de la liste [1, 2, 3, 4, 5, 6], produite par range(1, 7), il s’agit donc de la factorielle de 6.
>>> import functools >>> functools.reduce(lambda x, y : x *y, range(1, 7)) 720
Guido Van Rossum propose de remplacer reduce() par une boucle :
>>> acc =1 >>> for i in range(1, 7) : acc*=i … >>> print(acc) 720
6. Entrées et sorties
6.1 Affichages avec print() Rév. 2023.05.01
Pour l’affichage à un endroit particulier de l’écran, voir le module curses et les séquences ECMA-48, qui permettent également certaines couleurs.
print("Ni!", 123) affiche la chaîne suivi du nombre
var =123 ; print(var, var *5)
var2 =123 ; print("Le double de", var2, "est", var2 *2)
Par défaut, la séparation des chaînes est l’espace, qu’il est possible de supprimer avec le paramètre sep ="" ou de remplacer par une chaîne de caractères.
>>> print("Nombre", 10, sep ="-*-") Nombre-*-10
Voir également les différents types de formatage.
print() sans saut de ligne
Pour supprimer le saut de ligne, utiliser le paramètre end ="" :
>>> import time >>> for i in range(5) : >>> print(i, end ="") ; time.sleep(1) 01234
Attention : une suite de print(chaine, end ="") ne sera affichée qu’à partir du premier print() contenant un saut de ligne. Pour imprimer effectivement une chaîne sans saut de ligne, utiliser flush=True. Cela permet par exemple une barre de progression horizontale :
>>> import time >>> for i in range(5) : >>> print("-", end="", flush=True) ; time.sleep(1)
Il est possible de remplacer une ligne imprimée par une autre en utilisant "\r" en début de seconde chaîne, qui doit être aussi longue que la première pour la recouvrir entièrement (en mode script uniquement) :
#! /usr/bin/python3 import time print("Attendre trois secondes", end="", flush=True) time.sleep(3) input("\rFini! ")
Écrire directement dans un fichier
Pour écrire dans un fichier, on peut utiliser son descripteur file=descr :
with open("fichier.txt", "w") as descr : print("012345", file =descr)
Le fichier ne sera édité que lors de la fermeture du fichier (au sortir de la structure). Les formatages de print() sont maintenus (saut de ligne, tabulation…).
Il existe une méthode plus expéditive :
print("@%§&$£", file =open("fichier.txt", "a"))
Notes : les modes d’écriture sont
- "w" pour le mode «ouvrir en écrasant un éventuel contenu»
- "a" pour le mode «ouvrir en ajoutant à un éventuel contenu»
repr(objet) représente un objet sous forme de chaîne, par exemple pour le stocker dans un fichier. Voir eval() pour l’opération inverse) :
>>> a ="C'est déjà ça" >>> a ; print(a) ; print(repr(a)) "C'est déjà ça" C'est déjà ça "C'est déjà ça"
6.2 Formatages de chaînes Rév. 2023.04
La concaténation est une façon d’inclure des variables dans une chaîne, en n’oubliant pas de transformer les nombres en chaînes avec str() :
>>> n =5 ; p =6 ; print(str(n) +" /" +str(p) +" =" +str(round(n /p, 3))) 5 /6 =0.833
La façon native de formatage de chaînes prévoit des champs avec % et rassemble les variables dans un tuple qui suit la chaîne formatée :
>>> n =5 ; p =6 ; print("%d /%d =%.3f" %(5, 6, 5/6)) 5 /6 =0.833
La façon la plus pratique est la chaîne de formatage f"{}", qui interprète et formate ce qui est inclus entre {} à l’endroit où elles apparaissent : {variable ou fonction:formatage} ; le formatage reprend la nomenclature de format() :
>>> n =5 ; p =6 ; print(f"{n} /{p} ={n /p:.3f}") 5 /6 =0.833
Formatage %
Il ressemble à la façon dont le C (et PHP, java…) formate ses chaînes, mais au lieu de faire suivre toutes les variables ou valeurs à la queue-leu-leu, celles-ci sont rassemblées dans un tuple précédé d’un signe «pour cent» %() contenant des valeurs ou des variables (nombre ou chaînes) :
>>> n =5 ; print("Imprimer le nombre %d et %.3f, son tiers" %(n, n /3)) Imprimer le nombre 5 et 1.667, son tiers
%s pour une sortie générale (chaîne ou nombre, entier ou «réel»)
%9s prévoit 9 caractères minimum pour la chaîne (éventuels espaces à gauche)
%-9s prévoit 9 caractères minimum pour la chaîne (éventuels espaces à droite)
%r entoure une chaîne de guillemets simples, un nombre sans
%c sort le caractère selon son code numérique : 65 =A, 66 =B, etc. (alternative à chr())
%d sortie entière (partie décimale tronquée) de base décimale
%+d force l’affichage du + pour les nombres positifs
%4d pour un minimum de 4 caractères
%-4d pour un minimum de 4 caractères, espaces vides éventuels à droite
%04d remplace les vides éventuels par des 0
%i semble un parfait synonyme de %d dans toutes ses déclinaisons
Note : en Python 3.0 et 3.1, print("%, d" %(1234)) permettait d’afficher la virgule séparatrice de milliers, ce que fait format() (voir ci-dessous)
%o sortie octale (base 8) : 55 → 67 (6 *8 +7) (soit oct() sans le préfixe "0o")
%x sortie hexadécimale (base 16 : 0…9, a, b, c, d, e, f) : 55 → 37 (3 *16 +5) (soit hex() sans le préfixe "0x")
%X sortie hexadécimale où les lettres sont majuscules (base 16 : 0 à 9, A, B, C, D, E, F)
Note : en Python 3.0 et 3.1, %b permettait la sortie d’un entier en binaire, ce que fait format() (voir ci-dessous)
%f sortie décimale (virgule flottante) 5.5 → 5.500000 (6 chiffres après la virgule par défaut)
%9.3f sortie sur 9 caractères, dont un pour le point et 3 pour les décimales
%09.3f la partie entière nulle sera comblée de 0 : 4.3 → 00004.300
%.f arrondit à l’unité selon les règles académiques : 5.5 → 6 – 6.5 → 6 !
Pour supprimer l’éventuel zéro avant le point décimal, il faut supprimer le 0 initial avec [1:]
>>> print(("%.3f" %(5/6))[1:]) .833
%e pour une sortie scientifique : 55 → 5,500000e+01
%% permet d’afficher le caractère %
La fonction format(valeur, "param")
Cette fonction (à ne pas confondre avec la méthode .format()) retourne un formatage plus précis. Le signe % n’est pas utilisé, mais la fonction peut être utilisée dans un tuple %(valeurs) pour le formatage natif "%s". Ces chaînes de formatage s’utilisent sans guillemets dans les chaînes de formatage f"{valeur:formatage}"
">43s" prévoit 43 caractères minimum pour la chaîne, un texte plus court se cale à droite, les espaces de remplissage à gauche
"->43s" prévoit 43 caractères minimum pour la chaîne, un texte plus court se cale à droite, les traits d’union de remplissage à gauche
"<43s" prévoit 43 caractères minimum pour la chaîne, le texte se cale à gauche
"*<43s" prévoit 43 caractères minimum pour la chaîne, un texte plus court se cale à gauche, les astérisques de remplissage à droite
"^43s" prévoit 43 caractères minimum pour la chaîne, texte centré si possible
"/^43s" prévoit 43 caractères minimum pour la chaîne, texte centré si possible ; les barres de remplissage de part et d’autre
".43" supprime ce qui excède les 43 premiers caractères d’un texte
"9d" neuf caractères sont réservés pour le nombre, aligné à droite
"+9d" le signe est toujours présent, le signe positif n’est pas sous-entendu
"=+9d" le signe se met au début du nombre, le nombre est aligné à droite
"*=9d" le signe * est répété dans tous les espaces libres
"*=+9d" le signe est affiché, puis le caractère de remplissage *, puis le nombre
"," affiche la virgule de séparation des milliers
"n" (à partir de python3.10?) selon les spécificités locales (virgule décimale)
"b" affiche un nombre sous sa forme binaire (0 et 1)
"019_b" sépare l’affichage binaire en quatre groupes de quatre (plus trois séparations _)
"o" affiche un nombre sous sa forme octale (0 à 7)
"x" affiche un nombre sous sa forme hexadécimale (0 à 9 et a à f)
"X" affiche un nombre sous sa forme hexadécimale (0 à 9 et A à F)
"#b" affiche le préfixe 0b devant le nombre binaire
"#o" affiche le préfixe 0o devant le nombre octal
"#x" affiche le préfixe 0x devant le nombre hexadécimal
".2%" présente un réel sous forme de pourcentage (à deux décimales en l’occurrence : format(.431, ".2%") donne '43,10%'
"*=17+.3f" réserve 17 caractères pour l’expression d’un nombre «réel», dont 3 pour la partie décimale, le signe est mis en première place, le reste est rempli de *
"*=15+.3e" réserve 15 caractères pour l’expression scientifique d’un nombre «réel» :
>>> format(123456.789, "@=+15.3e") '+@@@@@1.235e+05'
Plus de spécifications sur format() ici
La méthode .format()
La méthode .format() peut s’appliquer à une chaîne contenant des {} pour leur faire correspondre des valeurs :
>>> "{} et {}".format("Juliette", "Roméo") 'Juliette et Roméo'
Cette méthode permet de formater des données d’une chaîne dans la chaîne elle-même, selon la nomenclature de la fonction format(), à droite du «deux points» :
>>> "{:,} et {:.3e}".format(12345, 12000) '12,345 et 1.200e+04'
Un surplus de valeurs est ignoré, un manque déclenche l’erreur IndexError: Replacement index 1 out of range for positional args tuple
Il est possible de modifier l’ordre des champs (ici le troisième et le deuxième) :
>>> "{2:,} et {1:.3e}".format(12345, 12000, 123) '1.230e+02 et 12,000'
Pour un appel à une collection (liste, tuple ou chaîne), la préfixer d’une astérisque précise que ce sont les éléments de la collection qui sont concernés et non la collection en elle-même :
>>> amants =["Roméo", "Juliette"] >>> "{} et {}".format(*amants) 'Roméo et Juliette' >>> tup =("a", "b", "c") ; "{1} {0} {2}".format(*tup) 'b a c' >>> mot ="chien" ; "{4}{2}{0}{1}{3}".format(*mot) 'niche'
Pour appeler les éléments d’un dictionnaire, il faut préciser les clés dans les champs. L’appel au dictionnaire se fait avec deux astérisques, les clés ne prennent pas de guillemets :
>>> amants ={"lui":"Roméo", "elle":"Juliette"} >>> "{elle} et {lui}".format(**amants) 'Juliette et Roméo'
Chaînes de formatage f"{val/fct:formatage}"
L’objet f"{}" est une amélioration récente (python3.6) du formatage de chaînes par la méthode .format(), en incluant les valeurs et leur formatage aux endroits où elles doivent apparaître dans la chaîne. Le contenu de {} peut être un appel à une fonction (de python ou de l’utilisateur) ou une méthode de classe comme "machaine".lower(). L’éventuel : et ce qui vient après est une formule de formatage comme vu plus haut.
Il est possible de mixer ce nouveau formatage avec l’ancien (%_), mais s’il manque des données dans le tuple %(), aucune erreur n’est détectée, %_ à l’intérieur de f"" étant affiché tel quel.
Cet exemple affiche les valeurs de 0 à 15 en décimal, binaire et hexadécimal :
for i in range(16) : print(f"{i:2d} - {i:08b} - {i:X}")
Il est possible d’inclure une fonction dans un {champ}. Pour former une liste des points «Unicode» en heXadécimal majuscule des caractères d’une chaîne :
>>> [f"{ord(i):X}" for i in "Python’s Circus"] ['50', '79', '74', '68', '6F', '6E', '2019', '73', '20', '43', '69', '72', '63', '75', '73']
Pour évaluer une expression :
>>> f"{7 **2 -6}" '43'
Le formatage est celui de la fonction format(), auquel il faut ajouter :
>>> spam ="eggs" ; f"{spam=}" "spam='eggs'" >>> spam ="eggs" ; f"{spam = }" "spam = 'eggs'" >>> spam ="eggs" ; f"{spam=!s}" 'spam=eggs' >>> spam ="eggs" ; f"{spam!r}" "'eggs'"
Dans une chaîne formatée, {{ permet d’afficher { et }} affiche } ; f"{{{var}}}" permet de faire les deux :
>>> a =5 ; print(f"{{a}}") {a} >>> a =5 ; print(f"{{{a}}}") {5} >>> dic ={"ciné":"Monthy"} ; print(f"{dic['ciné']}") Monthy >>> dic ={"ciné":"Monthy"} ; print(f"{{dic['ciné']}}") {dic['ciné']} >>> dic ={"ciné":"Monthy"} ; print(f"{{{dic['ciné']}}}") {Monthy} >>> dic ={"ciné":"Monthy"} ; print(f"{{{dic['ciné']} Python}}") {Monthy Python}
Notes Ajouts 2023.09-11
1. conformément à l’affectation des chaînes contenant des guillemets, utiliser les guillemets simples ou \" à l’intérieur d’une f"" et les guillemets doubles ou \' à l’intérieur d’une f''. Il est encore possible d’utiliser les f"""triples guillemets""" pour y loger indifféremment ' ' et " ".
2. Il est possible de placer plusieurs variables dans une {expression} :
couv =["cuillère", "fourchette", "couteau"] for i in [0, 2, 1] : print(f"{couv[i]}")
3. Il est parfois nécessaire d’imbriquer des accolades, notamment pour agir sur le formatage d’une sortie (ici, le nombre de décimales avec :._f) :
for i in range(7) : print(f"{22 /7:.{i}f}")
4. Le formatage avec f"" est plus susceptible que celui avec "%_" :
>>> print("%d" %(22/7)) 3 >>> print(f"{22/7:d}") Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: Unknown format code 'd' for object of type 'float'
6.3 Saisies avec input()
input() permet la saisie d’une chaîne.
>>> input("What's your quest?") What's your quest?
Pour une fonction permettant la saisie des touches de fonctions et flèches, voir le module curses.
Saisie d’expression évaluables Rév. 2023.01.11
eval() évalue une expression Python exprimée sous forme de chaîne :
>>> eval("3 *8") 24
Combiné avec input(), eval() peut transformer une chaîne saisie en résultat :
>>> eval(input("Une fraction: "))
Il peut s’agir d’une fonction :
>>> eval("'Je vous ai compris !'.split()") ["Je", "vous", "ai", "compris", "!"]
Ne pas confondre eval() avec exec(), qui peut exécuter une série d’instructions contenues dans une chaîne ou dans un script sous forme de fichier.
Une saisie multiple est possible avec eval(input()) :
>>> a, b, c =eval(input("entrer trois nombres séparés par des virgules: ")) 1, 2, 3 # à entrer! >>> a 1 >>> b 2 >>> c 3
6.4 Fichiers-textes Rév. 2023.05.01
Le module os permet également de manipuler les fichiers.
Attention ! Les différents systèmes n’utilisent pas le même codage de fin de ligne (en fait fin de paragraphe) : octet 13 pour Amiga et Mac avant OSX, octet 10 pour Unix et OSX, et les deux octets 13 et 10 pour le DOS et Atari. L’ouverture (GNU/Linux) en lecture de fichiers-textes remplace les fins de ligne \r et \r\n par l’unique \n de Unix. Ce n’est qu’une intuition, mais la sauvegarde de fichiers-textes devrait transformer les fins de ligne dans le format du système sur lequel est installé python.
Pour charger un fichier-texte en préservant les fins de ligne originelles, voir fichiers binaires.
Sauvegarder un fichier
Voici la séquence historique pour écrire un texte dans un fichier :
descr =open("/chemin/fichier", "w") descr.write("ma chaîne à écrire") descr.close()
- descr =open("/chemin/fichier", "w") crée un fichier de texte en écriture ("w" pour "write"), en écrasant éventuellement un fichier de même nom, sans demande de confirmation ! À ce stade, le nom du fichier est inscrit dans le répertoire par défaut, de longueur 0 octet
- descr.write("une chaîne") prévoit l’écriture d’une chaîne dans le fichier ouvert, représenté par descr
- descr.close() ferme le fichier en inscrivant les données
Notes :
- Plusieurs descr.write() peuvent se succéder avant close(), ce qui produit une concaténation sans séparation entre les différentes chaînes écrites
- pour sauvegarder des nombres ou des collections, utiliser repr()
- descr.writelines(["Les sanglots longs","des violons","de l'automne"]) écrit bout à bout toutes les chaînes d’une liste, sans séparation ("Les sanglots longsdes violonsde l'automne"). Pour éviter cela :
descr.write(" ".join(maliste)) descr.write("\n".join(maliste))
Une façon plus nouvelle (à partir de python2.6) est d’utiliser la structure with … as … ; c’est lors de la sortie de la structure (réalignement du code avec with) que le fichier est refermé, avec sauvegarde de la chaîne dans le fichier :
with open("python.txt", "w") as descr : descr.write("Eggs & Spam") print("fait!")
Les chaînes sont donc stockées dans une variable-tampon et ne s’écrivent dans le fichier qu’après descr.close() ou lors de la sortie de la structure with. Pour anticiper l’écriture des données :
descr.flush() force l’enregistrement, sans refermer le fichier
Une troisième façon d’éditer un fichier est d’y écrire directement :
>>> print("Les sanglots longs des violons"), file=open("fichier.txt", "w"))
Attention : contrairement à descr.write(), print() ajoute une fin de ligne à la fin de la chaîne.
La seule ligne
open("essai.txt", "w").write("Une chaîne")
fonctionne, mais il n’est pas dit qu’elle soit légale : '__warningregistry__': {'version': 0}} apparaît dans le dictionnaire des variables vars().
descr =open(nom, "r+") ouvre un fichier en lecture et en réécriture, en écrasant les données à partir du début et laissant l’éventuel surplus intact.
>>> print("0123456789", file=open("fichier.txt", "w")) >>> print("abc", file=open("fichier.txt", "r+"))
transforme le contenu du fichier : abc3456789
Ajout à un fichier texte
descr =open("fichier.txt", "a") ouvre un fichier en ajout d’écriture, et le crée s’il n’existe pas encore. Idéal pour un fichier «log».
Il est possible de rediriger les sorties print() vers un fichier-texte en utilisant le module sys, mais c’est bien plus simple avec file =descr
with open("mon_fichier.txt", "a") as descr : # ouverture en ajout print([1,[2,3],4], file =descr)
ou
print("Ajout d'une chaîne", file =open("monfichier.txt", "a"))
descr =open(nom, "a+") ouvre un fichier en lecture et ajout
Tous ces modes utilisent l’UTF-8 par défaut. Pour charger un fichier préalablement sauvegardé en iso8859-1, il faut préciser l’encodage :
with open("mon_fichier.txt", "r+", encoding="latin-1") as fd : print(fd.read())
Cela n’est pas nécessaire pour les fichiers sans lettres accentuées puisque l’ASCII (avec des octets de 32 à 127 + 9, 10 et 13) est un sous-ensemble de l’UTF-8, de l’ISO8859 et tout autre encodage.
Note :
- l’encodage latin-1 peut lire l’UTF-8, mais chacun des deux octets codant les lettres accentuées seront décodés comme étant un seul caratère. Huître âgée codée en UTF-8 se lit Huître âgée en latin-1.
- il semblerait que latin-1 permette de lire la table de caractères cp1252 (y compris les octets 128 à 159) ; pour ces deux encodages, voir iso8859.htm
Lecture d’un fichier-texte
En préalable, sauvegardons un fichier-texte, qui servira pour tous les exemples de cette section :
print("àbc\ndèf\nghî\n\njkl", file=open("fichier.txt", "w"))
descr =open("fichier.txt", "r") ouvre un fichier en lecture, descr est ici son descripteur et c’est à partir de cette variable que vous pourrez lire des données avant de refermer le fichier avec cette variable
fd =open("fichier.txt") # "r" est facultatif txt =fd.read() fd.close() print(txt)
- descr =open("/chemin/fichier") ouvre un fichier de texte en lecture (le "r" pour "read", est facultatif)
- txt =descr.read("une chaîne") lit effectivement le contenu dans la variable txt
- descr.close() ferme le fichier
Tant que le fichier n’est pas fermé, il est possible d’interroger le descripteur :
descr.name renvoie le nom du fichier
descr.mode renvoie le mode d’ouverture du fichier ("r" par défaut)
descr.closed contient False tant que le fichier est ouvert, True sinon
desc.fileno() est un numéro d’ouverture de fichier
Pour lire un fichier avec la structure with / as (depuis python2.6) :
with open("fichier.txt") as fd : texte =fd.read() print(texte) print(fd.closed) # True
- la variable texte contient déjà le contenu du fichier avant sa fermeture
- au sortir de la structure, le fichier est bien refermé
chaine =fd.read(n) permet de ne charger qu’un nombre limité de n caractères, en positionnant le pointeur n caractères plus loin, permettant la lecture du fichier par morceaux.
with open("fichier.txt") as fd : print(fd.read(4)) print(fd.read(6))
Pour lire le fichier-texte ligne par ligne :
with open("fichier.txt") as fd : while -1 : ligne =fd.readline() # "readline()" sans "s" if not ligne : break # si la ligne est vide print(ligne, end="") # "ligne" contient déjà le retour de ligne
Il existe une syntaxe très simple pour traiter une à une les lignes d’un fichier-texte :
with open("fichier.txt") as fd : for ligne in fd : print(ligne, end="")
ou même
for ligne in open("fichier.txt") : print(ligne, end="")
Il est possible de placer toutes les lignes du fichier-texte dans une liste :
with open("fichier.txt") as fd : lignes =fd.readlines() # "readlines()" avec "s" print(lignes)
…mais ces quatre dernières façons contiennent le caractère de fin de ligne.
Il est plus confortable de lire le fichier en entier et découper soi-même le fichier en lignes, qui se retrouvent dans une liste :
with open("fichier.txt") as fd : lignes =fd.read().split("\n") print(lignes)
donne :
['àbc', 'dèf', '', 'ghî', 'jkl', '']
6.5 Fichiers binaires et relatifs
Fichiers «binaires»
Dans un fichier «binaire», les 256 octets possibles peuvent être écrits ou lus. Les données sont passées sous la forme d’un objet bytes. Il faut spécifier le mode d’ouverture b (pour binaire ou bytes) : rb, wb, ab et rwb, éventuellement suivis de +. Pour sauvegarder des données sous forme de fichier binaire :
data =bytes([0, 255, 178, 56, 94]) with open("dummy.bin", "wb") as fd : fd.write(data)
Pour lire un tel fichier :
with open("dummy.bin", "rb") as fd : data =fd.read() print(data)
donne :
b'\x00\xff\xb28^'
Voir également le module os permettant de manipuler les fichiers.
Fichiers "relatifs"
Les fichiers relatifs ("random access") sont des fichiers binaires directement accessibles à n’importe quel endroit et pour une quantité précisée :
with open("fichier.dat", "rb") as fd : octets1 =fd.read(16) octets2 =fd.read(20)
octets1 contient les 16 premiers octets, octets2 les 20 suivants… un pointeur ayant conservé le décalage résultat de la lecture des 16 premiers octets. Le système est le même en mode écriture, mais soyez attentif à bien introduire des objets bytes :
octets1 ="a\x01b\x02" ; octets2 ="c\x03d\x04e\x05" with open("fichier.dat", "rw") as fd : fd.write(octets1) fd.write(octets2)
Il est possible de pointer un endroit particulière avant de commencer à lire (ou à écrire) dans un tel fichier :
descr.seek(43, 0) pointe le 44e octet (le premier est le décalage 0) à partir du début du fichier (positionnement absolu ; ,0 est facultatif)
descr.seek(13,1) pointe 13 octets plus loin, à partir de la position courante (1 : positionnement relatif)
descr.seek(5,2) pointe 5 octets vers le début du fichier (2 : positionnement relatif rétrograde)
descr.seek(-100) pointe 100 octets avant la fin fichier
descr.tell() retourne le décalage courant
Notes : le fichier s’étend selon les besoins : un seul octet écrit au décalage 10 **6 générera un fichier d’un mega +1 octets ; les octets non écrits sont des octets nuls.
Voir la recette sur l’utilisation de tels fichiers.
7. Fonctions «utilisateur»
En mathématique (analyse), une fonction retourne une et une seule valeur numérique. Pour beaucoup de langages de programmation, les «fonctions» peuvent renvoyer plusieurs valeurs, une chaîne, l’adresse d’un tableau, voire ouvrir un fichier ou imprimer un message d’erreur.
Les noms de fonction suivent les mêmes règles que les noms de variable.
En Python, une fonction est toute partie de code qui peut être écrit en dehors d’une structure et qu’il est possible d’appeler de n’importe quel endroit du programme. Il est même possible de les écrire dans un autre fichier, appelé module, que l’on pourra importer (voir modules utilisateurs).
Les fonctions servent à ne pas répéter le même code à plusieurs endroit du programme, ce qui constitue un gain de place, permet une maintenance plus simple et une lecture plus globale, le cœur du programme pouvant être une séquence très courte et assez explicite. À condition de prévoir de façon adéquate les fonctions lecture(), trouver_liens(), ajout() et suivant(), le noyau d’une application peut ressembler à ceci :
cpt =0 while cpt < 10 : page =lecture("fichier" +str(cpt)) liens =trouver_liens(page) sortie =ajout("fichiersortie", liens) cpt +=1 suivant()
Certains mots sont réservés à python et ne peuvent donc pas être utilisés comme nom de fonction (ou de variable) : and - as - assert - break - class - continue - def - del - elif - else - except - exec - finally - for - from - global - if - import - is - lambda - not - or - pass - raise - return - try - while - with - yield
7.1 Fonction anonyme lambda
lambda x : permet une définition simple de fonction :
carre =lambda x : x **2 print(carre(5))
Les fonctions anonymes permettent également de définir une fonction à l’intérieur d’une instruction de Python.
>>> filter(lambda x : 117 > ord(x) > 111, "python") 'pt'
Cette écriture peut être remplacée par une liste en compréhension :
>>> "".join([x for x in "python" if 117 > ord(x) > 111])
qui peut se traduire : joindre les lettres contenues dans la chaîne "python" à condition que leur code ASCII se situe entre 111 et 117.
Note : La fonction lambda est conservée en python3, malgré le vœu de Guido Van Rossum de l’abandonner.
7.2 Fonction classique def
def fonction(arg1, arg2…) permet la définition d’une fonction définie par l’utilisateur. La valeur envoyée dans la fonction peut être un nombre, une variable ou une autre fonction. La valeur est reçue dans la définition de la fonction par une autre variable (ici arg) multipliée par deux et affichée à l’écran. Exemple :
def double(arg) : print(arg *2) print(double(5))
Il est possible de définir une valeur par défaut. La réponse de l’exemple suivant sera 20 parce qu’aucune valeur n’est précisée dans l’appel :
def double(arg =10) : print(arg *2) double()
La définition de la fonction doit être placée avant l’appel. L’exemple suivant retournera une erreur, affiche() n’étant pas connu de l’interpréteur lorsque celui-ci rencontre sortir() :
def sortir(nbr) : affiche(str(nbr) *nbr) # le nombre répété nbr fois sous forme de chaîne sortir(5) def affiche(chaine) : print(chaine)
Dans cette correction de script, même si la fonction affiche() suit la fonction sortir(), elle est connue de l’interpréteur lorsque celui-ci rencontre sortir() :
def sortir(nbr) : affiche(str(nbr) *nbr) # le nombre répété nbr fois sous forme de chaîne def affiche(chaine) : print(chaine) sortir(5)
return
return arrête une fonction (par exemple dans une condition) et retourne la valeur None
return expression arrête la fonction et retourne la valeur (ou un tuple si les valeurs sont multiples)
def double(var) : return var *2 for i in range(1, 11) : print(double(i))
Dans le cas où plusieurs variables par défaut sont définies, il est possible de préciser la variable que l’on passe :
def mult(x =1, y =10) : return x *y print(mult(x =3)) print(mult(y =4))
Variables locales et globales
Une variable initialisée à l’intérieur d’une fonction est locale : elle est séparée de celles utilisées dans le reste de l’application ou initialisées dans d’autre fonctions. Remarquez la différence entre les deux print(ch) :
def chaine() : # affiche 'eggs' ch ="eggs" print(ch) ch ="spam" chaine() # affiche 'eggs' print(ch) # affiche 'spam'
Si l’on veut qu’une variable initialisée dans une fonction soit utilisable par le reste de l’application (et dans d’autres fonctions), il faut indiquer juste après la ligne def l’instruction global reprenant la liste des variables globales séparées par des virgules (global var1, var2, var3 pour plusieurs variables globales).
def chaine() : global ch ch ="eggs" ch ="spam" chaine() # la fonction chaîne() modifiera la variable globale ch print(ch)
Passage d’arguments spéciaux
Une suite de valeurs sera rassemblée en un tuple si la variable de réception d’une fonction est préfixée par * et une suite d’affectations en un dictionnaire si la variable de réception est préfixée par ** , avec composition possible (les valeurs doivent être écrites avant les affectations) :
def quoi(ch, *tp, **dc) : print(ch, type(ch)) print(tp, type(tp)) print(dc, type(dc)) quoi("quoi?", 1, 2, 3, a=2, c=5, d=9)
Les valeurs pour le tuple peuvent faire défaut, le tuple sera vide ; les affectations peuvent manquer, le dictionnaire sera vide.
Le passage de valeurs réceptionnées par un tuple fonctionne également avec la nouvelle structure match /case (python 3.10).
Récursivité
Une fonction peut s’appeller elle-même (récursivité). Il est alors indispensable de prévoir (au moins) une sortie :
def fact(x) : if x ==0 : return 1 return x *fact(x -1) # la fonction s’appelle elle-même print(fact(10))
…la valeur étant décrémentée jusqu’à 0 ; à ce moment, le calcul x *(x-1) *(x-2)… *1 est effectué et retourné.
Fonction locale
Il est possible de définir une fonction locale, en l’incluant dans une autre fonction. Cette fonction locale ne peut être appelée en dehors de la fonction qui l’inclut :
def euler(max) : def fact(x) : if x ==0 : return 1 return x *fact(x -1) somme =0 for k in range(max) : somme +=1 /fact(k) return somme print(euler(16)) print(fact(5)) # déclenche l’erreur NameError: name 'fact' is not defined
Modification de fonctions internes à python
Il est possible de modifier les fonctions internes de Python en les redéfinissant : int(), float(), complex(), oct(), hex(), len(), abs(), divmod() en redéfinissant ces fonctions.
Si l’on désire que la fonction len() retourne la longueur en octets plutôt qu’en caractère :
def len(x) : long =0 if type(x) ==type("") : # seulement pour les chaînes x= bytes(x, "utf-8") for i in x : long +=1 return long
…quoiqu’il vaudrait mieux créer une fonction-utilisateur ayant son propre nom.
Note : il est impossible de redéfinir les mots réservés.
7.3 Génération avec yield Rév. 2024.03
yield fait d’une fonction un générateur. Dans l’exemple qui suit, zenon() ne dispose pas de return, qui l’interromprait, mais yield qui renvoie une valeur en suspendant l’exécution, en conservant les dernières valeurs de n et somme. Ce n’est qu’avec l’itération de la boucle for que la boucle while refait un tour, sans repasser par n =1 ; som =0 comme le ferait une fonction lors d’un nouvel appel. zenon() permet ainsi la création d’un générateur de longueur indéfinie. En pratique, le nombre 1 sera atteint, les «réels» étant rarement illimités en informatique.
#! /usr/bin/python3 def zenon() : n =1 ; somme =0 while True : somme +=(1 /(2 **n)) yield somme n +=1 print(type(zenon())) for i in zenon() : dummy =input(i)
Une autre façon d’utiliser les valeurs successives d’un générateur est de lui appliquer la fonction next() :
gen =zenon() # générateur dans la variable 'gen' while True : dummy =input(next(gen)) # succession des données appui sur une touche
L’intérêt d’un générateur défini avec yield est son économie de la mémoire : les valeurs renvoyées sont oubliées, et les suivantes ne sont pas encore calculées, contrairement à une liste définie en compréhension, qui conserve dans une variable tous les éléments générés. L’inconvénient est qu’il n’est pas possible de revenir sur une valeur précédente sans relancer le générateur, alors que chaque élément d'une liste est rappelable à volonté.
L’exemple suivant donne les approximations successives du nombre e, par la formule Σ(1/x!), soit 1 +1 +1/2 +1/6 +1/24… vers 2,71828… ce qui permet de voir que la série converge assez rapidement vers la valeur :
#! /usr/bin/python3 def euler() : def facto(x) : # fonction incluse pour le calcul des factorielles if x==0 : return 1 return x *facto(x -1) somme =0 ; i =0 while True : somme += 1 /facto(i) yield somme i +=1 cpt =0 for i in euler() : # approximation du nombre e cpt +=1 ; input(f"{cpt:4d} : {i}")
Le recours aux générateurs permet de ne pas limiter une série a priori ou de manipuler de très grosses collections avec peu de mémoire. En cas de très gros fichiers-textes qu'on ne pourrait stocker en mémoire mais que l'on pourrait traiter ligne par ligne :
def textfile(fichier) : for ligne in open(fichier) : yield ligne for i in textfile("data.csv") : print(i[:-1]) # pour l'exemple
7.4 Modules «utilisateur»
Voir ici pour des informations plus précise sur les modules.
Il est possible de regrouper des fonctions qui seront utilisées par plusieurs applications dans un fichier (par exemple monmodule.py, l’extension .py est nécessaire) que l’on appelle dans une application avec import monmodule (sans .py). Le nom d’n module ne peut comporter de ponctuation (point, trait d’union…).
Sauvegardons par exemple les deux fonctions déjà vues plus tôt, dans le fichier matsup.py :
#! /usr/bin/python3 # écrit en python2 2006.12.20 -- réécrit en python3.7.3 -- www.jchr.be -- copyleft GPL3 -- """MATSUP : un module juste pour essayer""" # définition d'une constante e =2.718281828459045 def fact(n) : """Renvoie la factorielle d'un nombre : 1, 1, 2, 6, 24, 120, 720…""" if n ==0 : return 1 else : return n *fact(n -1) def euler(i) : """Valeur approchée de 'e' après 'i' itérations : 1, 2, 2.5, 2.66…, 2.70833…""" somme =0 for k in range(i) : somme +=1 /fact(k) return somme
Dans le mode interactif, importons-le, et interrogeons-le :
>>> import math, matsup >>> matsup.euler(10) 2.7182815255731922 >>> matsup.e 2.718281828 >>> import math >>> math.e 2.7182818284590451
Vous avez la possibilité (et le devoir) de commenter votre module """dans chaque fonction""", ce qui donnera :
>>> import matsup ; help(matsup.fact) fact(n) """Renvoie la factorielle d'un nombre : 1, 1, 2, 6, 24, 120…"""
Essayez également help(matsup) et help(matsup.euler).
On annule un module avec del module ou del(module)
Notes
- tout comme une fonction peut en appeler une autre, le module sauvegardé sous le nom mod1.py peut en importer le module mod2.py. L’appel à une fonction x() du second module se fait avec mod1.mod2.x().
- l’importation cherche le module dans le répertoire courant, puis dans les librairies de votre version de Python, à l’adresse /usr/lib/python3.9 pour python3.9 en Unix&c°. Attention : n’appelez jamais un script du nom d’un module que vous importez : il se chargerait lui-même en lieu et place du module (en créant un fichier .pyc, qu’il faudra détruire).
- il est possible de placer à l’adresse /usr/lib/python…/ (avec les droits de super-utilisateur) un module que vous avez réalisé ; pour qu’il se compile en votremodule.pyc, il faut lancer Python dans ce mode et importer le module.
- dans une phase de mise au point (en mode interactif), réimporter un module après modifications ne produit aucun effet, il faut le recharger (une fonction renommée s’ajoute à l’ancienne, qui ne sera pas supprimée durant la session ; l’important est que les modifications soient enregistrées)
import importlib # à ne faire qu'une fois par session importlib.reload(votremodule) # doit avoir été préalablement importé
del module décharge un module. Attention : l’importation d’un module fabrique sa compilation sous l’extension .pyc, dans le sous-répertoire __pycache__. C’est ce fichier sous forme compilée qui sera ensuite chargé.
7.5 Décorateurs Nv 2024.03.09
Un décorateur permet d’insérer une fonction à l’intérieur du code d’une autre fonction. Le code est ici réduit autant que possible pour ne pas compliquer l’exemple, l’intérêt peut paraître assez limité.
Prenons un script sans décorateur. Pour comparer le temps utilisé par plusieur boucles, il est possible d’entourer chacune de celles-ci par un appel à une empreinte temporelle et l’impression de la différence :
#! /usr/bin/python3 import time t =time.thread_time() for i in range(1000) : for j in range(10000) : pass print(time.thread_time() -t) t =time.thread_time() for i in range(10000) : for j in range(1000) : pass print(time.thread_time() -t)
Pour n’écrire les deux lignes contenant time.thread_time() qu’une fois, il est possible de créer une fonction générique deco() qui recevra chaque boucle à mesurer si elle est incluse dans une fonction précédée de @deco. Python sait alors que les fonctions à tester doivent passer par le «décorateur» def deco(), dans laquelle une sous-fonction est écrite pour mesurer le temps de récursion des boucles imbriquées.
#! /usr/bin/python3 import time def deco(fct) : def mesurer() : t =time.thread_time() fct() print(time.thread_time() -t) return mesurer @deco # essayer sans def a(): for i in range(1000) : for j in range(10000) : pass a() @deco def b(): for i in range(10000) : for j in range(1000) : pass b()
@deco reçoit successivement comme paramètre les fonctions a() et b() à travers la «variable» fct - la sous-fonction mesurer() intègre chaque fonction entre deux mesures temporelles.
- commenter
@deco (le faire précéder de #) annule ce processus - il suffit d’insérer
@deco avant la définition de n’importe quelle fonction pour que son appel «fasse le détour» par def deco()
Cette technique permet donc d’économiser du code en cas de fonctions partiellement identiques, en réservant le code identique à un «décorateur». Elle peut également servir à tracer les fonctions décorées pour un débogage :
#! /usr/bin/python3 def traque(fonct) : def tracer() : print("(début de %s)" %fonct.__name__) fonct() print("(fin de %s)" %fonct.__name__) return tracer @traque def mafct(): print("cœur de la fonction «décorée»") mafct()
- %fonct.__name__ contient le nom de la fonction traitée (mafct en l’occurrence).
8. Programmation-objet
La programmation orientée objet (POO) vous permet de définir et utiliser vos propres variables structurées ainsi que leurs comportements. On commence par définir une classe d’objets, c’est-à-dire un objet générique et ses composants :
- les attributs, variables internes à une classe d’objet définis à partir des paramètres passés lors de la définition d’un nouvel objet (instanciation)
- les méthodes, fonctions spécifiques à cette classe d’objet. La première est obligatoire et s’appelle __init__(). Elle est appelée chaque fois qu’une nouvelle instance (variable-objet) est définie, elle en réceptionne les valeurs et en fixe les attributs.
Les avantages sont nombreux : une fois qu’une variable est instanciée par UneVariable =VotreClasse(paramètres) il en découle une série d’attributs qui auront été définis rien que pour eux, les objets bénéficient de méthodes qui leur sont propres ; il est même possible de (re)définir les opérateurs classiques comme les + , * , ==…
8.1 Classe et attributs
Le premier script ci-dessous définit une classe d’individus définis par une hauteur et un poids, et pour lesquels l’indice de masse corporelle sera automatiquement calculé.
La méthode __init__ y réceptionne les valeurs par les paramètres ht et pds. Le paramètre self représentera l’objet dans toute la définition de la classe.
Chaque argument de réception est ensuite fixé à l’objet par self. Pour nouvel objet créé dans le script par une instanciation (par exemple x =individu(178,75)), x.hauteur représentera sa taille et x.poids son poids. Un troisième attribut est aussi calculé : l’indice de masse corporelle (le poids divisé par le carré de la taille, arrondi ici à une décimale), x.IMC.
#! /usr/bin/python3 class individu : def __init__(self, ht, pds) : self.hauteur =float(ht)/100 self.poids =float(pds) self.IMC =round(self.poids/self.hauteur/self.hauteur, 1) toto =individu(178,75) print(toto.hauteur, toto.poids, toto.IMC)
L’exécution de ce premier script de programmation-objet permet de voir que dès que l’objet toto est défini (instanciation), l’attribut IMC est automatiquement calculé et disponible.
callable(individu) renvoie True parce qu’individu est une classe
isinstance(toto, individu) renvoie True si toto appartient à la classe individu
del(toto) ou del toto détruit l'instance toto
del(individu) ou del individu détruit la class individu
8.2 Méthodes
Une classe ne se limite en général pas à la définition de __init__(). L’intérêt de la programmation objet est aussi de définir des méthodes spécifiques aux objets d’une classe. Dans le script qui suit, une autre façon de calculer le poids idéal est défini. On peut appeler la méthode comme s’il s’agissait de la fonction d’un module importé : individu.ideal(toto), mais il est plus pratique d’utiliser toto.ideal(), python sachant que toto appartient à la classe individu, dont ideal() est une méthode.
#! /usr/bin/python3 class individu : def __init__(self, sx, ht, pds) : """sexe : M =1, F =2 ; hauteur: en mètres ; poids : en kilos""" self.sexe =sx if (1 >self.sexe or self.sexe >2) : raise("1 pour les hommes ; 2 pour les femmes") self.hauteur =float(ht) self.poids =float(pds) self.IMC =round(self.poids /self.hauteur /self.hauteur, 1) def ideal(self) : taux =1 # calcul pour un homme if self.sexe ==2 : taux =.9 # révision à la baisse pour les femmes return ((self.hauteur *100 -150) *3 /4 +50) *taux def diagnostic(self) : if self.IMC <15 : return "- Maigreur extrême (<15)" if 15 <=self.IMC <18.5 : return "- Maigreur (15-18,5)" if 18.5 <=self.IMC <25 : return "- Poids idéal (18,5-25)" if 25 <=self.IMC <30 : return "- Surpoids (25-30)" if 30 <=self.IMC <35 : return "- Obésité modérée (30-35)" if 35 <=self.IMC <40 : return "- Obésité sévère (35-40)" if 40 <=self.IMC : return "- Obésité morbide (>40)" def anamnese(self) : print("Pour adultes, exceptés sportifs et femmes enceintes ou qui allaitent") print("Sexe:", self.sexe, "-", self.hauteur,"m,", self.poids,\ "Kg - Poids idéal (autre formule) :", self.ideal()) print("IMC:", self.IMC, self.diagnostic()) toto =individu(1, 1.78, 75) toto.anamnese()
Notes :
- Le paramètre représentant l’objet pourrait être nommé autrement, mais self est intéressant parce qu’il est reconnu par les éditeurs qui disposent de la coloration syntaxique.
- Il est possible de définir une ou plusieurs classes dans un fichier séparé, que l’on importe comme un module. Le nom de la classe doit alors être préfixée du nom du module, mais pas un attribut ou une méthode. Pour le module MesClasses.py contenant la définition de classe individu :
import MesClasses toto =MesClasses.individu(2, 1.78, 75) print (toto.IMC) # attribut toto.anamnese() # méthode
8.3 Surcharge des opérateurs
Python n’additionne pas de la même manière les nombres et des chaînes… La programmation orientée objet permet de redéfinir comment +, -, %, / … doivent agir : c’est ce qu’on appelle «surcharger» un opérateur.
Pour modifier l’addition + entre deux instances d’une classe, on écrit une méthode __add__(self, other) et l’on décide en fin de méthode ce qui sera renvoyé. Ce mode d’addition n’affectera que les objets de la classe dans laquelle il aura été défini. Voici un exemple de surcharge d’opérateur où l’on réécrit la comparaison d’égalité __eq__ pour comparer des indices de masse corporelle avec une tolérance de 5% :
#! /usr/bin/python3 class individu : def __init__(self, ht, pds) : self.hauteur =float(ht) self.poids =float(pds) self.IMC =round(self.poids /self.hauteur /self.hauteur, 1) def __eq__(self, other) : # redéfinition de l'opérateur "==" if abs(self.IMC -other.IMC) /max(self.IMC, other.IMC) <.05 : return True else : return False a =individu(1.81, 82) b =individu(1.81, 85) c =individu(1.81, 89) print(a.IMC, b.IMC) print(a ==b ==c) print(a ==c)
Comme il s’agit d’un test, True ou False est retourné, ce qui permet d’utiliser le test dans une condition : if (a ==b) :
Python est capable, à partir d’une opération binaire, d’enchaîner deux opérations : dans le cas ci-dessus, a ==b ==c renvoie True parce que a ==b et b ==c mais a ==c n’est pas vrai car trop différents.
Méthodes unaires (self) | ||||||||||
- | __neg__() | + | __pos__() | ~ | __invert__() | |||||
Méthodes de comparaison (self, other) | ||||||||||
< | __lt__() | == | __eq__() | > | __gt__() | in | __contains__() | |||
<= | __le__() | != | __ne__() | >= | __ge__() | |||||
Méthodes binaires classiques (self, other) | ||||||||||
+ | __add__() | - | __sub__() | / | __div__() | // | __floordiv__() | |||
* | __mul__() | ** | __pow__() | % | __mod__() | / | __truediv__() | |||
& | __and__() | | | __or__() | ^ | __xor__() | << | __lshift__() | |||
>> | __rshift__() | |||||||||
Méthodes binaires abrégées (self, other) | ||||||||||
+= | __iadd__() | -= | __isub__() | /= | __idiv__() | //= | __ifloordiv__() | |||
*= | __imul__() | **= | __ipow__() | %= | __imod__() | /= | __itruediv__() | |||
&= | __iand__() | |= | __ior__() | ^= | __ixor__() | <<= | __ilshift__() | |||
>>= | __irshift__() |
8.4 Autres fonctions
En cours d’écriture
NouvelObjet =object() permet de créer une classe à partir de rien.
super() permet de se référer à la classe parente et à utiliser ses méthode sans utiliser son nom
issubclass(sub, classe) retourne True si sub dérive de classe
hasattr(inst, "attr") retourne True si l’attribut existe pour l’objet
getattr(inst, "attr") retourne la valeur d’un attribut pour une instance
setattr(inst, "attr", val) fixe une valeur à l’attribut d’une instance, comme obj.attr =val
delattr(inst, "attr") détruit l’attribut d’une instance (nom est une chaîne) = del obj.attr
isinstance(obj,classe) retourne True si l’objet (instance) appartient à la classe
callable(expr) retourne True si l’expression est appelable : les classes et les fonctions internes ou définies par les utilisateurs ou dans les modules (et doit alors éventuellement être préfixées), et False pour les variables ; les mots réservés (for, if, def…) retournent SyntaxError s’il sont entourés de guillemets, une erreur sinon.
9. Introspection
Python permet l’inspection de son propre fonctionnement.
type() retourne le type d’une variable.
>>> type("spam") <class 'str'>
Attention : <class 'str'> n’est pas une chaîne, mais un objet de classe 'type' :
>>> type(type("spam")) <class 'type'>
En cas de test, on ne peut comparer un type avec une telle chaîne ; il faut donc comparer la réponse de type() à une classe de python ou au type d’un objet connu :
>>> type(12e2) =="<class 'float'>" False >>> type(12e2) ==float True >>> type(12e2) ==type(.1) True
Les classes internes à python sont bool, int, float, complex, str, bytes, tuple, list, dict, set, map, range, filter, type, enumerate.
Note :
- ces termes ne sont pas réservés et peuvent donc servir comme nom de variable, ce qui empêche toute comparaison utile avec type()
- bien que type((x for x in range(5))) renvoie <class 'generator'>, le mot generator n’est pas défini (Python 3.11.2) :
>>> type((x for x in range(5))) ==generator Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'generator' is not defined
Il est cependant possible de créer cette variable :
>>> generator =type(x for x in (0,)) >>> generator <class 'generator'>
Le type des instances de classes définies par l’utilisateur peut également être identifiées de cette manière :
class MaClasse : pass x =MaClasse() type(x) ==MaClasse
isinstance(élément, objet) renvoie True si l’élément est bien une instance de l’objet (interne à Python ou défini par l’utilisateur) :
>>> isinstance([1, 5, 16], list) True
Cela fonctionne également pour les objets créés par l’utilisateur :
class MaClasse : pass x =MaClasse() isinstance(x, MaClasse)
Attention cependant, isinstance() est plus complaisant que type(), une instance d’une classe appartenant également à la classe parente :
>>> type(True) == type(5) False >>> isinstance(True, int) True >>> issubclass(bool, int) True
Il est possible de tester l’appartenance d’une instance à un ensemble de classes en même temps (l’appartenance à une seule des classes proposées dans le tuple suffit) :
>>> isinstance("43", (float, complex, int)) False >>> isinstance(43, (float, complex)) False >>> isinstance(True, int) True
Note : python ne suit pas la théorie mathématique, puisqu’un entier ne fait pas partie des «réels» et qu’un «réel» ne fait pas partie des complexes. Les booléens True et False font en revanche partie des réels : ils valent respectivement 1 et 0 dans une opération arithmétique :
>>> True +2 3
exec() permet d’exécuter du code contenu dans une chaîne ou dans un script python sous forme de fichier :
>>> exec("from math import sin, pi ; print(sin(pi/4))") 0.707106781187 >>> exec(open("monfichier.py").read())
Faites très attention à la dernière commande : que se cache-t-il dans monfichier.py ?
callable(expr) retourne True si l’expression est appelable : les classes et les fonctions internes ou définies par les utilisateurs ou dans les modules (éventuellement être préfixées), et False pour les variables :
>>> callable(print) True
Les mots réservés (for, if, def…) retournent SyntaxError s’il sont entourés de guillemets, False sinon.
dir() liste de fonctions de python dans la portée locale :
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']
Note : dans une fonction, liste les fonctions imbriquées ; dans une classe, liste ses méthodes et attributs.
dir('__builtins__') liste les noms de méthodes et d’attributs définis pour une instance (ici : '__builtins__')
Dictionnaires de variables globales ou locales
vars() est un dictionnaire des variables, où chaque variable est décrite comme une chaîne (son nom) associée à sa valeur .
>>> pi =3.1415926 >>> vars() {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'pi': 3.1415926}
Attention ! Son contenu diffère selon l’endroit du script, vars() ne voyant que les variables locales dans une classe ou fonction.
globals() est le dictionnaire des variables globales. En dehors de toute fonction ou de classe, globals() ==vars()
locals() est le dictionnaire des variables locales d’une fonction ou d’une classe. Dans une fonction ou classe, locals() ==vars()
Identifiants de variables et de données
id(var) donne l’identifiant d’une variable. Dans une fonction, une variable locale a un id() différent de son homonyme global. À chaque réaffectation d’une variable, l’identifiant change, comme par exemple la variable de récursion dans une boucle à chaque itération :
for i in range(3) : print(id(i))
Note : l’ id() d’une fonction se retrouve en hexadécimal dans vars().
hash(var) donne l’identifiant de la donnée contenue dans une variable ; deux variables de même valeur partagent la même valeur "hash".
a =56 **.5 ; b =56 **.5 print(hash(a), hash(b))
A. Documentation
Documentation dans le logiciel Python
Dans le mode interactif (obtenu en saisissant python3 dans une console), vous en saurez plus sur la licence avec
>>> print(copyright)
>>> print(license())
help rentre dans l’aide interactive
help(fonction) informe sur une fonction (écrite ici sans parenthèse) ou un module interne ou chargé
help("mot-clé") les guillemets sont obligatoires pour les informations sur un mot-clé (for, while…)
Quelques lettres suivies de deux tabulations [tab] affichent toutes les fonctions commençant par ces lettres :
>>> comp[tab][tab] compile( complex(
Il est possible de produire un fichier texte à partir d’une console :
$python3 -c "help('while')" > while.txt
Pour un module interne à Python, ou installé séparément :
python3 -c "import sqlite3 ; help('sqlite3')" > sqlite3.txt
Voir le fichier consacré aux modules.
Documentation sur votre système GNU+Linux
Si Python3 est installé, saisir man python3 dans une console renseigne sur les différentes manières de lancer Python.
Le script /usr/bin/pydoc permet la consultation d’informations sur les fonctions, modules, mots-clés :
pydoc divmod présente succinctement la fonction divmod, équivaut à help() plus haut
pydoc math présente succinctement les fonctions du module math, équivaut à help() plus haut
pydoc keywords liste les mots-clés sur lesquels il y a une information
pydoc -g lance une interface graphique pour une navigation dans le système d’aide
/usr/share/doc/python3 et /usr/share/doc/python3.12 contiennent quelques fichiers d’aide si le paquet python3-doc a été installé.
Note : le nom des paquets et les adresses peuvent varier selon les distributions et les versions.
Documentation sur Internet
- docs.python.org (en anglais) renvoie vers les pages tutorial, références…
- wiki.python.org et son Aperçu de la littérature francophone
- python.lycee.free.fr: débuter avec Python au Lycée
- fr.wikibooks.org/wiki/Python proposent plusieurs tutoriels pour débutants
- Apprendre à programmer avec Python pdf 4,4Mo de Gérard Swinnen
- Dive into python en français, site en (re)construction