Blog ENI : Toute la veille numérique !
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
💥 Les 22 & 23 novembre : Accès 100% GRATUIT
à la Bibliothèque Numérique ENI. Je m'inscris !
  1. Livres et vidéos
  2. Python et Raspberry Pi
  3. Python : bases et concepts avancés
Extrait - Python et Raspberry Pi Apprenez à développer sur votre nano-ordinateur (3e édition)
Extraits du livre
Python et Raspberry Pi Apprenez à développer sur votre nano-ordinateur (3e édition)
2 avis
Revenir à la page d'achat du livre

Python : bases et concepts avancés

Hello World

Au fil des années, Python s’est imposé en tant que langage de choix enseigné dans les universités et les écoles d’informatique. Comment ce langage a-t-il gagné une telle popularité ? Python se rapproche le plus de ce qui ressemble à du pseudo-code, c’est-à-dire un moyen formel de décrire une fonction sans pour autant faire référence à un langage de programmation en particulier. La syntaxe du langage est très facile à aborder et afin de familiariser le lecteur avec celle-ci, il est temps de commencer par le traditionnel programme Hello world. Tous les programmes apparaissant entre parenthèses et en italique sont mis à disposition depuis la page Informations générales ou sur Github à l’adresse https://github.com/EditionsENI. Voici le premier d’une très longue série (Chapitre_2/hello.py) :

#!/usr/bin/env python3 
hello = 'Hello world!' 
print(hello) 

La première ligne du fichier est ce qui s’appelle en jargon UNIX un shebang. Ce caractère #! indique deux choses au système d’exploitation : que ce script est exécutable et que, pour exécuter le code qui se trouve en dessous de la première ligne, le système d’exploitation doit utiliser l’interpréteur indiqué...

Les types de base : int, float, str et bool

1. L’entier : int

Python n’est pas un langage de programmation fortement typé, comme peuvent l’être d’autres langages de programmation comme Java ou C++. Cependant les types de base embarqués dans le langage sont assez complets. Python 3 comprend ce qu’est un entier (int), un flottant (float) et une chaîne de caractères (str).

Le type int est le plus largement répandu. Il comprend l’ensemble de tous les entiers qu’il est possible d’assigner à une variable en fonction d’une architecture donnée. Sur une architecture 32 bits par exemple, une variable pourra contenir un entier compris entre -2^31 et 2^31 - 1 (soit un nombre compris entre -2 147 483 648 et 2 147 483 647). Pour outrepasser cette limite de stockage, le type long est normalement sollicité. Cependant, la PEP 237 indique qu’à partir de Python 3, tous les nombres sont considérés comme des entiers de type int. Les nombres de type long n’existant plus. Dans les versions de Python 2.x, le type long doit encore être utilisé lorsqu’il s’agit de manipuler de très grands nombres. Nous ne nous attarderons pas sur ce type mais sachez qu’il existe.

Qu’est-ce que cela implique pour le programmeur ? La conversion est transparente et, la plupart du temps, vous n’avez pas à vous soucier de savoir si vous manipulez un int ou un long, Python s’en charge pour vous :

>>> type(2 ** 500) 
<class 'int'> 
>>> type(2 ** -500) 
<class 'float'> 
>>> type(2 ** 500000) 
<class 'int'> 

Autre point important concernant les entiers : il est possible d’assigner un entier à une variable en utilisant trois syntaxes ou bases :

  • la base décimale : la plus connue, l’entier doit commencer par un chiffre compris entre 1 et 9.

  • la base octale : l’entier doit commencer par le chiffre 0 accompagné de la lettre o puis d’un chiffre compris entre 1 et 7.

  • la base hexadécimale : l’entier doit commencer par 0x suivi soit d’un chiffre compris entre 0 et 9 soit d’une lettre comprise entre A et F.

Ces trois syntaxes sont largement expliquées...

Les structures de données : list, dict, tuple

Au cours de l’élaboration d’un programme, l’une des problématiques est de savoir comment organiser les données de celui-ci dans des « compartiments », plus communément appelés structures de données. Les structures de données implémentées dans Python sont assez simples et complètes : il est possible de ranger ses données dans une list, un tuple (l’équivalent d’une list avec la propriété d’être immuable), un dict (souvent appelé dictionnaire ou tableau associatif), et enfin un set, qui est en fait un ensemble mathématique.

1. La liste

Une liste est une séquence ordonnée d’éléments. L’indice du premier élément da la liste commence à partir de 0, l’indice du deuxième élément est 1, et ainsi de suite. La taille d’une liste est dynamique : une liste peut s’agrandir ou se rétrécir à la demande, lorsque le programme en a besoin.

Pour créer une liste, l’utilisation de la syntaxe avec les crochets [ ] est conseillée, même s’il est aussi possible d’utiliser la fonction list() :

>>> l = list([1, 2, 3]) 
>>> l 
[1, 2, 3] 
>>> l = [4, 5, 6] 
>>> l 
[4, 5, 6] 

Python n’étant pas un langage fortement typé, il est donc possible qu’une liste contienne n’importe quel type d’élément...

Les instructions, conditions et boucles

Qu’est-ce qu’une instruction ? Derrière ce nom se cachent les mots-clés les plus fréquemment utilisés pendant le développement d’un programme qui rendent la programmation impérative possible : Est-ce que cette variable est égale à 2 ? Est-ce que cette chaîne de caractères est vide ? Comment parcourir les éléments de cette liste ?

Avant de continuer, un point capital à ne surtout pas négliger lors de l’écriture des exemples de cette section et pour le reste du livre : l’indentation du code.

Elle est incontournable en Python, et elle fait même partie intégrante de celui-ci. Un programme dont le code n’est pas correctement indenté est considéré comme invalide. Cela peut paraître troublant au début mais bien indenter son code doit devenir un réflexe et une habitude. Lorsque vous tapez un bloc de code commençant par une structure de contrôle (if, for, while), IDLE indente et espace automatiquement le code la plupart du temps pour satisfaire aux exigences de l’interpréteur Python.

1. La condition if

Commençons par l’instruction la plus simple, et la plus fréquente : l’instruction si ou if. Dans IDLE, cliquez sur New - New File et écrivez le code suivant :

1 #!/usr/bin/env python3 
2 var = 1 
3  
4 if var == 1: 
5     print("var est égal à 1") 

Exécutez le code. Le message var est égal à 1 doit s’afficher dans la console IDLE. L’instruction if permet donc de poser une condition, de la vérifier et d’exécuter le bloc de code qui se trouve en dessous de celle-ci le cas échéant. Sauvegardez le code dans le répertoire Chapitre_2 et appelez le fichier if.py.

2. La condition else

Instruction complémentaire au si : le sinon ou else. Nouvel exemple :

1 #!/usr/bin/env python3 
2 var = 2 
3  ...

Les opérateurs

Comme bien d’autres langages de programmation, Python permet de comparer des valeurs entre elles par le biais d’opérateurs. Il est assez rare d’avoir recours à tous les opérateurs du langage pendant l’écriture d’un programme. Néanmoins, voici ceux offerts par le langage. Attention cependant : Python surcharge certains opérateurs en fonction du type de données manipulé.

1. Opérateurs arithmétiques

  • + : lorsqu’il est utilisé avec des entiers ou des flottants, il ajoute et retourne le résultat de l’opération. Cet opérateur peut aussi être utilisé avec d’autres types tels que les chaînes de caractères :

>>> a = 'Bonjour.' 
>>> b = 'Comment allez vous ?' 
>>> a + ' ' + b 
'Bonjour. Comment allez-vous ?' 

Il est aussi possible de l’utiliser avec au moins deux listes. Dans ce cas, l’opérateur + fusionne le contenu des listes, c’est-à-dire qu’une nouvelle liste est créée, le contenu des listes y est ajouté et la nouvelle liste est retournée :

>>> a = [1, 2, 3]  
>>> b = [4, 5, 6]  
>>> c = [7, 8, 9] 
>>> a + b + c 
[1, 2, 3, 4, 5, 6, 7, 8, 9] 
  • - : lorsqu’il est utilisé avec des entiers ou des flottants, il soustrait et retourne le résultat de l’opération. Cet opérateur peut aussi être utilisé avec la structure de données set afin de faire la différence entre deux sets. Grâce à cette opération, il compare, supprime les doublons trouvés dans deux structures de données et retourne le contenu du premier set, les doublons en moins :

>>> a = set([1, 2, 3, 3])  
>>> b = set([3, 3, 4, 5])  
>>> a 
{1, 2, 3} 
>>> b 
{3, 4, 5} 
>>> a - b 
{1, 2} 
  • * : lorsqu’il est utilisé avec des entiers ou des flottants, il multiplie et retourne le résultat de l’opération. Il peut aussi être utilisé avec une chaîne de caractères. Dans ce cas, la chaîne de caractères est répétée...

La classe

L’aisance d’utilisation de Python rend l’écriture de petits programmes assez simple sans pour autant se soucier de la réutilisation de briques logicielles. Pour remédier à ce problème, les concepteurs du langage ont pris soin d’ajouter tous les ingrédients nécessaires pour transformer Python en un langage moderne. Parmi eux figurent la programmation orientée objet et le concept de classe. Attention cependant : le but de ce chapitre n’est pas d’enseigner les bases de la programmation orientée objet, mais d’expliquer de manière claire et succincte comment fonctionne une classe en Python, d’apprendre à écrire des classes et les quelques subtilités qu’elles comportent. Il est nécessaire de connaître certaines bases pour programmer avec le Raspberry Pi, notamment pour la manipulation de bibliothèques graphiques telles que tkinter

Ce chapitre n’aborde que très brièvement le sujet des classes qui est, rappelons-le, très vaste en Python. En ce sens, si vous souhaitez approfondir au maximum, nous vous conseillons le livre de Sébastien Chazallet, Python 3 - Les fondamentaux du langage disponible aux Éditions ENI, et notamment le chapitre sur le modèle objet qui explique dans les moindres détails la programmation orientée objet en Python et tout ce que les classes peuvent accomplir : l’héritage multiple, la surcharge d’opérateur, le polymorphisme, les attributs dynamiques...

Autre point important : la programmation orientée objet est totalement optionnelle en Python. Il est tout à fait possible d’écrire des programmes de plusieurs dizaines de lignes sans avoir recours à la création et à la manipulation d’une seule classe.

Tout d’abord, qu’est-ce qu’une classe ? Dans le langage courant, une classe est un moyen de représenter un objet issu du monde réel dans le domaine de la programmation : une personne, une voiture, une température. Pratiquement n’importe quel objet peut être représenté à l’aide d’une classe. L’exemple souvent utilisé pour expliquer la programmation orientée objet et le concept de classe est celui...

Les fonctions

On parle souvent de fonction ou de routine en programmation lorsqu’il s’agit de factoriser une tâche répétitive. En effet, appeler une fonction revient à réutiliser un bloc de code plusieurs fois dans un programme. En Python, deux mots-clés existent pour définir une ou plusieurs fonctions au sein d’un programme : def et lambda. Examinons leurs différences.

1. Définir une fonction

Le mot-clé def crée une fonction et l’assigne à un nom. Voici la syntaxe associée à def :

1 def nom_de_la_fonction(argument1, argument2,… argumentX):  
2        corps de la fonction  
3        return résultat 

Une fonction def est avant tout un objet de type function. Par exemple :

>>> def fois_deux(x):  
...   return x * 2   
...   
>>> fois_deux  
<function fois_deux at 0x7fcc13a66950>  
>>> type(fois_deux)  
<class 'function'>  
>>> dir(fois_deux)  
['__annotations__', '__call__', '__class__', '__closure__',  
 '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__',  
 '__doc__', '__eq__', '__format__', '__ge__', '__get__',  
 '__getattribute__', '__globals__', '__gt__', '__hash__',  
 '__init__', '__kwdefaults__', '__le__', '__lt__', '__module__',  
 '__name__', '__ne__', '__new__', '__qualname__', '__reduce__',  
 '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',  
 '__str__', '__subclasshook__'] 

La déclaration d’une fonction def commence par le nom de la fonction, avec entre parenthèses les arguments que celle-ci accepte. Le corps de la fonction est, quant à lui, indenté, comme la plupart des déclarations en Python. Le but d’une fonction est avant tout de traiter les arguments passés en paramètre, de calculer...

La syntaxe en compréhension

L’élégance de la syntaxe du langage Python est souvent encensée, notamment pour son écriture qui, pour les férus de mathématiques et d’algèbre, ressemble plus souvent à des formules qu’à du code. Guido van Rossum a obtenu une maîtrise en mathématiques à l’université d’Amsterdam avant d’orienter sa carrière vers le développement logiciel. Pourquoi est-ce important de rappeler ce fait ? Car cela a grandement influencé la syntaxe du langage, notamment la syntaxe en compréhension. Cette syntaxe permet notamment de déclarer une liste en filtrant ses éléments afin d’en extraire seulement le contenu souhaité. Elle s’étend aux dictionnaires, aux sets, aux tuples et aux générateurs. 

Expliquer l’essence d’une liste en compréhension et pouvoir écrire une liste soi-même nécessite la décomposition d’un exemple simple. Une des tâches les plus récurrentes en programmation est le tri de données, et essentiellement de structures de données : itération, assignation, condition.

Déclarons une liste composée de 10 chiffres :

>>> ma_liste = list(range(1, 11)) 
>>> ma_liste ...

Itérateur et générateur

Vous serez souvent amené à manipuler des listes, la plupart du temps de taille assez réduite, c’est-à-dire contenant très peu d’éléments, entre 10 et 100. Dans de tels cas, le système d’exploitation alloue de la mémoire afin d’y stocker les n éléments contenus. Cela ne pose aucun problème pour des traitements nécessitant peu d’espace mémoire. Seulement, lorsque la liste atteint une taille gigantesque, disons plus d’un million d’éléments, comment pallier le problème d’espace mémoire, ou du moins le contourner ? D’autant plus que le Raspberry Pi n’est généralement pas l’environnement idéal pour effectuer des traitements gourmands en mémoire virtuelle, la taille de celle-ci allant de 256 Mo à plus de 4 Go sur les derniers modèles.

En voici la preuve dans le REPL :

>>> 2**50  
1073741824  
>>> list(range(0, 2**30))   
Traceback (most recent call last):  
  File "<stdin>", line 1, in <module>  
MemoryError 

La création d’une liste de 1073741824 éléments demanderait beaucoup trop de mémoire. Python lève alors l’exception MemoryError quand la taille de la structure de données à créer est bien trop importante pour le système.

Un itérateur s’avère alors indispensable. En Python, un itérateur est avant tout une enveloppe qui sert d’interface entre une structure de données et le programme. Pour créer cette enveloppe, on utilise soit le mot-clé iter, soit la fonction __iter__(). Dans les deux cas, l’objet retourné est de type iterator :

>>> range(0, 2**30).__iter__()  
<range_iterator object at 0x7f74e73feb10>  
>>> iter(range(0, 2**30))  
<range_iterator object at 0x7f74e73fe510> 

Selon la structure de données manipulée, ce type varie : list_iterator pour une liste, tuple_iterator pour un tuple, set_iterator pour un set, et ainsi de suite. Ces itérateurs ont chacun un nom distinct, mais le type de base reste pour autant iterator :

>>>...

La gestion des exceptions

Une exception est le plus souvent levée lorsqu’un programme tente d’effectuer une opération illicite ou dangereuse. En programmation, malheureusement, il existe une large palette d’opérations dangereuses qui peuvent amener un programme à subitement s’arrêter : lecture d’un fichier qui n’existe pas, lecture d’un indice trop grand dans un tableau trop petit, récupération d’une clé dans un dictionnaire dont la valeur n’existe pas, etc.

Pour pallier ce problème, Python offre une gestion des exceptions native au langage et gérée à travers quatre mots-clés :

  • try, permet d’essayer d’exécuter un morceau de code pouvant générer une exception.

  • except, fonctionne de concert avec try et, lorsqu’une exception est levée, l’intercepte et continue, ou non, l’exécution du programme.

  • raise, lève une exception pendant l’exécution du programme.

  • finally, s’utilise à la suite d’une instruction try/except afin de toujours exécuter un bloc de code, qu’il y ait une levée d’exception ou non.

En pratique, comment est-ce que cela fonctionne ? Depuis le REPL, essayons de lire un fichier qui n’existe pas sur le Rasbperry Pi. Comme expliqué pmus loin, la fonction open() ouvre un fichier afin de lire son contenu :

>>> open('/etc/passwd', 'r')  
<_io.TextIOWrapper name='/etc/passwd' mode='r' encoding='UTF-8'> 

Lorsque le fichier est présent sur le disque, Python retourne un descripteur de fichier afin de manipuler le fichier en question. Le résultat est différent quand la ressource demandée n’est pas accessible :

>>> open('nexistepas', 'r')  
Traceback (most recent call last):  
  File "<stdin>", line 1, in <module>  
FileNotFoundError: [Errno 2] No such file or directory: 'nexistepas' 

Notez le nom de l’exception levée par Python : FileNotFoundError. Il existe...

L’import des modules avec le mot-clé import

Vous avez certainement déjà remarqué dans les pages précédentes le mot-clé import. La fonction première de ce mot-clé est d’importer un ou plusieurs modules. Dans vos programmes, ainsi que dans le REPL, import rend accessibles les fonctions ou variables contenues dans le module importé :

>>> import random   
>>> random.random()  
0.3751871473556778  
>>> import sys   
>>> sys.maxsize   
9223372036854775807 

L’import de plusieurs modules est possible en séparant par une virgule les noms des modules à importer :

>>> import os, re, cmd  
>>> os; re; cmd;  
<module 'os' from '/usr/lib64/python3.8/os.py'>  
<module 're' from '/usr/lib64/python3.8/re.py'>  
<module 'cmd' from '/usr/lib64/python3.8/cmd.py'> 

Pour lister les fonctions exposées et offertes par un module, on utilise la fonction dir() en lui passant en paramètre le nom du module à inspecter :

>>> import string  
>>> dir(string)   
['ChainMap', 'Formatter', 'Template', '_TemplateMetaclass',  
 '__builtins__', '__cached__'...

Le gestionnaire de contexte

Depuis la version 2.6, Python implémente la gestion de contexte à l’aide de deux mots-clés : with et as. À noter que cette gestion de contexte s’accompagne aussi d’un protocole.

Cependant, à quoi sert la gestion de contexte ? La meilleure définition serait : la gestion d’une ressource, que ce soit un fichier, un flux d’entrée/sortie, un socket réseau, sans avoir à explicitement fermer la ressource lorsqu’elle n’est plus utilisée. La gestion de contexte simplifie grandement le code, le rendant ainsi plus court et moins complexe à écrire.

Un exemple vaut toujours mieux qu’une longue explication. En Python, l’ouverture et la fermeture d’un fichier sont des opérations assez courantes. Dans ce cas, la fonction open() est utilisée afin de soit lire, soit d’écrire dans un fichier. Elle prend en paramètre deux valeurs :

  • La première est le chemin du fichier.

  • La seconde est le mode d’ouverture du fichier, qui peut être multiple : ’r’ pour la lecture, ’w’ pour l’écriture (qui a aussi pour effet d’écraser le contenu existant du fichier) et ’a’ pour ajouter du contenu à un fichier.

Le fichier est normalement refermé une fois l’opération terminée via un appel à la fonction close() (Chapitre_2/with_1.py) :

1 #!/usr/bin/env...

Conclusion

Ce chapitre est un condensé important de plusieurs concepts : structures de données les plus courantes du langage, fonctions avancées du langage telles que la programmation orientée objet, syntaxe en compréhension appliquée à une liste, itérateurs, gestionnaire de contexte, etc. Plus vos programmes sont complexes, plus la maîtrise et l’apprentissage de ces notions prennent tout leur sens.