Comme Gustave Thibon refusant d'écrire son dernier livre au prétexte que tout a déjà été écrit, je ne souhaite pas ici ré-écrire un autre tutoriel sur Python. Je me contenterai comme à mon habitude de retenir les bases et quelques subtilités sans trop d'explications histoire de faire travailler ses neurones et combler quelques trous de mémoire.
Trêve de bavardage. La plupart des distributions Linux disposent d'une version de python installée, la question est de savoir la quelle. Ouvrez une console et tapez ...
$ python3 --version
Python 3.10.6
On peut être surpris de ne pas écrire simplement
$ python
. C'est en fait une question de compatibilité avec les versions antérieure à python3.
À ce jour la dernière version de python est 3.11 mais mon système ne me la propose pas par défaut mais rien ne vous empêche de l'installer.
# apt install python3.11
Celle-ci ne viendra pas remplacer la version installée sur votre ordinateur mais il est possible de changer de version par défaut ou d'appeler une version explicitement comme expliqué ci-après.
Pour être tout à fait exacte on n'utilise pas python en console, on demande à la console d’exécuter l'interpréteur python, c'est le mode interactif et ça se passe ainsi:
$ python3
Python 3.10.6 (main, Mar 10 2023, 10:55:28) [GCC 11.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
Ou si vous préférez une autre version que celle installée par défaut
$ python3.11
Python 3.11.0rc1 (main, Aug 12 2022, 10:02:14) [GCC 11.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
Les trois chevrons >>>
attendent que vous écriviez quelques instructions en python.
>>> 3*(5+2)
21
>>>
Bon je ne m'attarde pas plus sur le mode interactif qui est un outil instantané très pratique pour des tests ou des calcules à la volée, il est tout à fait capable de se souvenir des variables et autres fonctions que vous lui déclarerez ... tant qu'il reste ouvert. Une fois l'interpréteur fermé (quit()
ou CTRL+D) il oubliera tout ce que vous lui avez apprit.
On nomme généralement les fichiers de script python avec le suffixe .py. Il s'agit plus d'une convention que d'une nécessité pour que nous, humains, puissions les reconnaître d'un seul coup d’œil. Le système lui n'a pas besoin de ce détail pour savoir à qui il a affaire, le système lit l'entête du fichier; le shebang.
Sur une distribution Linux il est indispensable d'indiquer un shebang en entête de fichier.py, tous vos scripts python devraient commencer par quelque chose comme ça:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
La première ligne indique à l'interpréteur quelle version de python utiliser, c'est le shebang à proprement parler. Si comme ici on ne précise pas le suffixe de version (3.xx) c'est la version par défaut du système qui sera utilisée. Si vous souhaiter utiliser une version en particulier il suffira d'indiquer le chemin où elle se trouve, comme ceci: #!/usr/bin/python3.11
.
Si vous ne savez pas où est installé python un petit
$ whereis python3.11
peut aider
La seconde ligne demande à utiliser exclusivement l'encodage de caractères en utf-8. Cette dernière est facultative mais nous assurera, à nous francophones, que nos caractères accentués s'afficheront correctement.
Pour que python puisse interpréter un fichier.py ce dernier doit être configuré avec les droits à l’exécution :
$ chmod +x fichier.py
.
Nous n'avons plus qu'à appeler le script depuis un terminal.
$ ./script.py
Je vous ai dit ci-dessus que sur "Linux il est indispensable d'indiquer un shebang", en fait j'ai menti. Vous pouvez tout à fait appeler un fichier sans sheebang comme ça
$ python3 script.py
mais c'est plus long et rien dans votre code n'indique quelle version utiliser.
# commentaire simple
""" commentaire
sur plusieurs
lignes """
Au delà du commentaire, les trois guillemets permettent de documenter les fonctions les classes et les modules et de rendre cette documentation accessible par le biais de la fonction native help().
def fonk():
"""" Cette fonction fait ceci cela """
pass
help(fonk)
retournera en console
Help on function fonk in module __main__:
fonk()
" Cette fonction fait ceci cela
(END)
En python l'incrémentation (var++) et la décrémentation (var--) n'existent pas.
On déclare une variable de la manière la plus simple:
nbr = 5
txt = "un peu de texte"
Il est possible de déclarer plusieurs variables d'un seul coup:
size = w, h = 500, 300
print(size)
(500, 300)
print(w)
500
print(h)
300
Identifier une variable pour connaitre son type:
var = 1.5
print(type(var))
<class 'float'>
Comparer une variable pour vérifier son type
var = 1.5
print(isinstance(var,int))
False
print(isinstance(var,float))
True
Une variable a une portée locale au bloc dans le quel elle a été déclarée
nbr = 3
def f1():
nbr += 2
print(nbr)
f1()
UnboundLocalError: local variable 'nbr' referenced before assignment
Le mot clé "global" permet de rendre une variable accessible dans l'ensemble du script.
nbr = 3
def f1():
global nbr
nbr += 2
print(nbr)
f1()
5
À noter qu'il existe également le mot clé "nonlocal" qui s'utilise avec des fonctions imbriquées. Sa portée va jusqu'à la fonction englobante parente
def f1():
nbr = 3
def f2():
nonlocal nbr
nbr += 2
f2()
print(nbr)
f1()
5
En mode interactif uniquement il est possible de récupérer le retour de la dernière instruction grâce au caractère souligné (_).
>>> a = 1.618
>>> b = 6922.47
>>> a*b
11200.556460000002
>>> 10+_
11210.556460000002
Cette variable doit être considérée comme une variable en lecture seule par l'utilisateur. Ne lui affectez pas de valeur explicitement — vous créeriez ainsi une variable locale indépendante, avec le même nom, qui masquerait la variable native et son fonctionnement magique.
chaînes de caractères (tuto python.org)
formatage (tuto python.org)
Il est possible de combiner variables et chaînes de caractères sans passer par la concaténation grâce au caractère f (ou F) avant l'ouverture des guillemets:
var1 = "maison"
var2 = "mites"
var3 = "femmes"
print(f"Ma {var1} est envahie par des {var2}")
Là ou d'autres langages utilisent les accolades, en python c'est l'indentation qui délimite les blocs d'instruction.
Généralement une indentation est constituée soit d'une tabulation soit de plusieurs espaces (2,4 ou 8 c'est selon). En python la convention demande d'utiliser 4 espaces. Paramétrez votre éditeur pour qu'il se comporte ainsi (Avec Sublime Text c'est tout en bas à droite).
Si vous avez mélangé tabulation et espaces dans votre code vous aurez le message explicite:
TabError: inconsistent use of tabs and spaces in indentation
Une simple condition if elif else
age = 50
if age < 18:
print("vous êtes mineur")
elif age >= 18 and age <= 65:
print("vous êtes majeur")
else :
print("Vous êtes vieux")
Une condition if else en écriture simplifiée
majeur = "oui" if age >= 18 else "non"
print("Êtes vous majeur?", majeur)
Une simple boucle for ...
for n in range(3):
print(n)
... affichera dans un terminal
0
1
2
Une simple boucle while ...
n = 0
while n < 3:
print(n)
n += 1
... affichera dans un terminal
0
1
2
reponse = "a"
match reponse:
case "a" :
print(1)
case "b" :
print(2)
case "c" :
print(3)
Avec une boucle for ...
for n in range(3,0,-1):
print(n)
else:
print("zero")
... ou avec une boucle while ...
n = 3
while n > 0:
print(n)
n-=1
else:
print("zero")
... les deux afficheront dans un terminal:
3
2
1
zero
Une listes en compréhension est une liste créée à partir d'une liste initiale sur laquelle on applique un filtre. Ici nous allons extraire les nombres pairs de la liste initiale et les multiplier par 2.
initiale = [1,2,3,4,5,6,7,8,9]
comp = [i*2 for i in liste_initiale if i%2 == 0]
Comprenez comp = [opération itération condition] où la condition est optionnelle.
Définition d'une fonction à 1 argument
def cube(n):
return n**3
Un astérisque (*) devant un nom d'argument permet à la fonction de recevoir un nombre arbitraire d'arguments dans un tuple.
def f(*args):
print(type(args))
for i in args:
print(i)
f(1, 2, 3)
Retournera en console:
<class 'tuple'>
1
2
3
Deux astérisques (**) devant un nom d'argument permet à la fonction de recevoir un nombre arbitraire d'arguments nommés dans un dictionnaire.
def f(**kwargs):
print(type(kwargs))
for i in kwargs:
print(i, kwargs[i])
f(a=1, b=2, c=3)
retournera en console
<class 'dict'>
a 1
b 2
c 3
Tout script python est un module, celui que l'on appel en premier dans la console (ex: ./script.py
) est le module __main__. On peut donc appeler de script en script, d'autres scripts qu'on appellera des modules.
(tuto python.org) modules
(tuto python.org) survole des bibliothèques natives
(doc python.org) index des modules natifs
Supposons le module "testModule.py"
var = 1234
def f1():
return "je suis une fonction"
Maintenant dans un fichier quelconque on charge le module et on s'en sert:
import testModule
print(testModule.f1(), testModule.var)
Dans ces exemples on suppose que le fichier appelant et le fichier du module sont au même niveau, si le module est dans un sous répertoire on l’appellera ainsi
import dossier.module
On peut aussi, le temps de son utilisation, changer le nom de la variable d'accès au module:
import testModule as mod
print(mod.f1(), mod.var)
On peut aussi s'épargner de réécrire le nom du module, attention toute fois aux espaces de noms:
from testModule import *
print(f1(), var)
Il est également possible de n'importer que certains éléments:
from testModule import f1, var
print(f1(), var)
Dans tous les cas présentés le résultat en console sera le même:
je suis une fonction 1234
Comme je l'ai mentionné au début de ce chapitre le premier fichier que vous appelez dans la console pour démarrer votre application est le __main__, en fait il prend le nom de __main__ et donc en toute logique si le nom d'un fichier est __main__ alors c'est que c'est lui qui à été appelé en premier. Il est donc possible d’exécuter du code seulement si un fichier à été appeler directement ce qui peut être très pratique pour tester les modules ou les classes que vous êtes en train d'écrire.
Si je reprends l'exemple du module "testModule.py" on pourrait avoir ceci:
def f1():
return "je suis une fonction"
if __name__ == "__main__":
def f2(x):
print(x)
f2(f1())
Enfin depuis une console je n'ai plus qu'à appeler mon $ ./testModule.py
pour que le code s’exécute.
Depuis l'interpréteur python c'est l'interpréteur qui prend le nom __main__
La fonction "dir()" liste tous les types de noms : les variables, fonctions, modules, etc. Utilisée avec l'exemple ci-dessus ça donne ça:
import testModule
print(dir(testModule))
en console on obtient:
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'f1', 'var']
Depuis l'interpréteur python
help('modules')
Dans le cas d'un environnement virtuel il est préférable d'utiliser pip depuis une console
$ python3 -m pip list
>>> import os
>>> print(os.__file__)
/usr/lib/python3.8/os.py
Même si ce n'est pas recommandé, il peut arriver que l'on importe tout (*) de plusieurs modules du coup on ne sait plus très bien de quel module est appelée telle fonction/méthode
from toto import *
from titi import *
import inspect
a = fonctionBidule()
# de quel module est appelée la fonctionBidule()?
reponse = inspect.getmodule(fonctionBidule())
print(reponse)
Que ce soit pour lire ou écrire dans un fichier on commence toujours par appeler la fonction open(). Elle prend au moins un arguments, le chemin vers le fichier à ouvrir, mais on l'utilise généralement avec un second argument; le mode d'ouverture.
data = open("fichier.txt", "r")
Les différents modes d'ouverture de fichier en python sont semblables à ceux utilisés dans d'autres langages de programmation.
Notez que les modes r et t sont les deux modes par défaut ce qui signifie que
open("fichier.txt")
,open("fichier.txt", "r")
etopen("fichier.txt", "rt")
sont parfaitement identiques, de même queopen("fichier.txt", "r+")
se comportera exactement de la même manière queopen("fichier.txt", "w")
.
f = open("fichier.txt", "r")
data = f.read()
Rien de compliqué ici, on ouvre le fichier en mode lecture (r) et on récupère son contenu (en mode texte par défaut) dans la variable "data".
f = open("fichier.txt", "a")
f.write("Ceci")
f.write(" cela")
Cette fois ci on ouvre le fichier en mode ajout (a), le texte "Ceci cela" s'écrira dans le fichier sur la même ligne. Si l'on souhaite aller à la ligne suivante on ajoutera simplement un saute de ligne grâce à la combinaison "\n" soit directement dans le corps du texte (f.write("Ceci\ncela")
) soit explicitement (f.write("\n")
) chaque fois que nécessaire.
Jusqu'à présent nous avons ouvert des fichiers sans les refermer et ça c'est mal, surtout que c'est comme le réfrigérateur c'est pas bien compliqué, il faut juste le faire. Donc une fois qu'on à fini de travailler avec un fichier il faut le fermer.
f = open("fichier.txt")
data = f.read()
f.close()
Cependant le mot clé "with" permet de ne pas avoir à se soucier de ce détail.
with open("fichier.txt") as f:
data = f.read()
Définition d'une classe simple à deux attributs; une variable (n) et une méthode (calcule()).
class Cube:
n = 12
def calcule(self):
return self.n ** 3
On notera seulement deux choses, conventionnellement le nom de la classe commence par une majuscule et le mot clé self qui fait référence à l'objet de la classe.
La vocation du constructeur est d'initialiser les attributs lors de l'instanciation de la classe.
classe Puissance:
def __init__(self, libre, carre=2, cube=3)
self.libre = libre
self.carre = carre
self.cube = cube
Une variable de classe est commune à toutes les instances (objets) créées alors qu'une variable d'instance sera propre à chaque objet (instance de la classe). On les utilisera donc en connaissance de cause.
class Puissance:
carre = 2 # variable de classe
cube = 3 # variable de classe
def __init__(self, autre=0):
self.autre = autre # variable d'instance
if __name__ == "__main__":
p1 = Puissance(4)
p2 = Puissance(8)
print(p1.carre, p2.carre)
print(p1.cube, p2.cube)
print(p1.autre, p2.autre)
Affichera en console
2 2
3 3
4 8
Héritage simple:
class Maman():
def __init__(self):
self.nom = "Dubois"
self.prenom = "Cecile"
class Fille(Maman):
def __init__(self):
super().__init__()
self.prenom = "Eloise"
fille = Fille()
print(fille.nom, fille.prenom)
Dubois Eloise
Héritage multiple
class Lucy():
def __init__(self):
self.nom = "Lucy"
class Meme():
def __init__(self):
self.nom = "Meme"
class Maman():
def __init__(self):
self.nom = "Maman"
class Fille(Lucy, Meme, Maman):
def __init__(self):
super().__init__()
fille = Fille()
print(fille.nom)
Lucy
Déclarer une variable en la préfixant d'un tiret bas (_var) indique au développeur que cette variable ne devrait pas être appelée depuis l'extérieur de la classe mais ne change en rien le comportement de python qui agira avec elle comme avec n'importe quelle variable. En fait c'est une convention.
Par contre, préfixer une variable de deux tirets bas (__var) (et au plus un tiret bas à la fin) permet à python de préfixer la variable par le nom de la classe et ainsi inclure la variable dans l'espace de nom de la classe. Démonstration:
class Maman:
def __init__(self):
self.fonk1()
self.__fonk2()
def fonk1(self):
print("Maman 1")
def fonk2(self):
print("Maman 2")
__fonk2 = fonk2 # copie privée de fonk2()
class Fille(Maman):
def fonk1(self):
print("Fille 1")
def fonk2(self):
print("Fille 2")
f = Fille()
f.fonk1()
f.fonk2()
Fille 1
Maman 2
Fille 1
Fille 2
On pourrait se demander pourquoi utiliser Pip pour installer des modules Python alors qu'il est tout à fait possible de les installer avec "apt" comme n'importe quel autre paquet Linux. Pour ma part je pense que c'est étroitement lié aux environnements virtuels qui permettent d'isoler vos différents projets et de les protéger de versions différentes de Python et des modules utilisés.
Pip est un module python et devrait donc déjà être installé, pour le vérifier on peut essayer ça.
$ python3 -m pip --version
pip 23.1.2 from /home/user/.local/lib/python3.10/site-packages/pip (python 3.10)
Sur les distributions basées sur Debian (c'est le cas de Ubuntu) il est nécessaire d'installer pip par les dépôts:
# apt install python3-pip
Si la version n'est pas à jour:
$ python3 -m pip install --upgrade pip
Installer un module:
$ python3 -m pip install <nom_module>
Désinstaller un module:
$ python3 -m pip uninstall <nom_module>
Obtenir la liste des modules installés avec pip:
$ python3 -m pip list
Pour faire simple et rapide, un environnement virtuel est un répertoire que vous créez là où bon vous semble qui contiendra une copie des binaires, exécutables, modules, librairies, etc... dont vous allez vous servir pour créer votre application, ceci afin de garder un contrôle au cas par cas des différentes versions utilisés dans vos projets.
Créer un environnement virtuel est une opération plutôt simple.
$ python3 -m venv /chemin/vers/environnent/virtuel/
Oups,
The virtual environment was not created successfully because ensurepip is not
available. On Debian/Ubuntu systems, you need to install the python3-venv
package using the following command.
apt install python3.10-venv
You may need to use sudo with that command. After installing the python3-venv
package, recreate your virtual environment.
heureusement comme souvent avec Linux le message est clair: installer le paquet python3.10-venv et recommencer. À adapter à votre version de python bien sûr.
Ici j'ai laissé la version de python par défaut de mon système mais on peut demander à installer une version en particulier et c'est justement pour cela qu'on le fait.
$ python3.11 -m venv /chemin/vers/environnent/virtuel/
Activer l'environnement:
$ source /chemin/vers/environnent/virtuel/bin/activate
J'ai mis ici un chemin absolu mais il va de sois que l'on peut faire plus simple si la commande est passée depuis le répertoire du projet
$ source bin/activate
on peut encore faire plus simple comme ça:$ . bin/activate
.
Et pour le désactiver:
$ deactivate
Pyinstaller est un module python qui permet de compiler les applications python en paquets, binaires, exécutables afin des les distribuer et permettre aux utilisateurs de les installer facilement.
pyinstaller -F -w -i=app.ico app.py
Si les clés d'un dictionnaire sont des chaînes de caractères il est plus simple de le déclarer avec le constructeur dict().
tel = {"Pierre":1234, "Paul":4567, "Jacques":7890}
# est identique à
tel = dict(Pierre=1234, Paul=4567, Jacques=7890)
Un fichier.py
import sys
if len(sys.argv) > 1:
print(f"il y a {len(sys.argv)-1} argument(s)")
else:
print("pas d'argument")
print(sys.argv)
L'appel de ce fichier dans une console avec les arguments a b c retournera
$ ./fichier.py a b c
il y a 3 argument(s)
['./fichier.py', 'a', 'b', 'c']
Si vous l'avez remarqué sys.argv retourne une liste et les listes sont indexées. Cette seconde approche est donc identique à la première, peut-être même plus élégante.
import sys
if len(sys.argv[1:]) > 0:
print(f"il y a {len(sys.argv[1:])} argument(s)")
else:
print("pas d'argument")
print(sys.argv)
action_avance = "hue"
action_arrete = "hoo"
def action(str):
var = "action_"+str
try:
print(eval(var))
except:
print(var, "n'est pas une variable!")
action("avance")
action("arrete")
action("miaule")
Affichera en console:
hue
hoo
action_miaule n'est pas une variable!
Une chaîne brute (r pour raw) ne peut pas se terminer par un nombre impair de caractères \ (backslash)
>>> print(r'toto\tutu')
toto\tutu
>>> print(r'toto\tutu\')
File "<stdin>", line 1
print(r'toto\tutu\')
^
SyntaxError: unterminated string literal (detected at line 1)
>>> print(r'toto\tutu\\')
toto\tutu\\
informations et des solutions de contournement
Supposons que le répertoire "test" contienne indifféremment des dossier et des fichier que nous souhaitons distinguer.
import os
# on se rends dans le répertoire test
os.chdir("test")
# on récupère le chemin dans la variable url
url = os.getcwd()
# on récupère le contenu dans la variable content
content = os.listdir(url)
# on boucle sur chaque élément trouvé
for elem in content:
if os.path.isfile(elem):
print("FI: ",elem)
else:
print("FO: ",elem)
Ce code retournera "FO: ..." pour chaque occurrence trouvée quelle soit un fichier ou un dossier.
L'erreur courante consiste à tester directement l'élément trouvé sans utiliser le chemin absolu if os.path.isfile(elem)
. Ce code tente de vérifier si "elem" existe à la racine de l'ordinateur, n'existant probablement pas il retourne tout le temps "False".
On corrigera notre erreur en utilisant le chemin complet de l'élément:
# on boucle sur chaque élément trouvé
for elem in content:
if os.path.isfile(os.path.join(url,elem)):
print("FI: ",elem)
else:
print("FO: ",elem)