Fichiers texte (CSV) en python

1 – Introduction

Un fichier « CSV » est un fichier texte qui respecte un certain format. En effet, comme son nom l’indique (comma seperated values) : valeurs séparées par des virgules. Le format CSV est très utilisé comme format par les tableurs (comme excel) et les logiciels de base de données pour les opérations d’import et d’export.

Dans cet article, on va découvrir le format « CSV », puis étudier la manière de manipuler ce genre de fichiers en Python.

2 – Fichiers CSV

Le contenu d’un fichier est un ensemble de lignes, où chaque ligne est composée de plusieurs valeurs séparées par des virgules (la virgule est le séparateur par défaut).

Exemple de fichier CSV : 

Sexe,Prénom,Année de naissance
M,Bob,1990
F,Alice,1980
F,Eve,2000

Le séparateur par défaut est la virgule « , ». Toutefois, on peut définir d’autres séparateurs comme : le point-virgule « ; », l’espace  » « , les deux points « : », … etc.

3 – Python et fichiers CSV

Comme les fichiers CSV sont des fichiers texte, il faut d’abord les ouvrir en mode lecture texte « r ». Pour cela, on utilise la fonction « open »

file = open("fichier.csv", "r")

Ensuite, on peut commencer à lire le contenu. Il existe différentes méthodes pour lire un fichier CSV selon ce qu’on veut récupérer : quelques caractères, une lignes, la listes des lignes, ou tout simplement tout. Soit « file » notre variable python qui est obtenue après ouverture du fichier :

  • file.read(n) : lire les « n » (au max) premiers caractères du fichier et les renvoie dans une chaîne de caratères.
  • file.readline() : lire la première ligne, c’est-à-dire, lire des caractère jusqu’à arriver au caractère « \n ».
  • file.readlines() : lire tout le fichier et le retourner sous forme de liste de lignes.
  • file.read() : lire tout le contenu du fichier et le retourner dans une chaîne de caractères.

Pour écrire dans un fichier texte, on utilise la méthode « write ».

  • file.write(texte) : écrire « texte » dans le fichier.

Enfin, pour fermer le fichier, on utilise la méthode « close ». C’est toujours important de fermer un fichier surtout après une écriture.

  • file.close()

Par exemple, pour remplacer le séparateur d’un fichier CSV de virgule « , » à dière « # », on a deux manières de faire :

3.1 – Sans le module csv

Pour lire le contenu d’un fichier CSV sans utiliser le module Python « csv », il faudra lire le fichier ligne par ligne et parser les lignes en python :

f = open("fichier.csv", "r+")     # mode lecture/écriture
lignes = f.readlines()
f.seek(0) # retour au début du fichier
for l in lignes:
# découper les lignes en plusieurs éléments
s = l.split(",")
# regrouper les éléments
ss = "#".join(s)
# écrire la ligne dans le fichier
f.write(ss)
f.close()

3.2 – Avec le module csv

Avec le module CSV, on utilise une la fonction « reader » qui génère un itérateur et qui itère sur les ligne du fichier. Cette fonction s’occupe également de parser les lignes. Ensuite, on utilise « writer » pour l’écriture.

import csv
f = open("fichier.csv", "r+")
r = csv.reader(f, delimiter=",")
lignes = list(r)
f.seek(0) # retour au début du fichier
w = csv.writer(f, delimiter="#")
w.writerows(lignes)
f.close() # fermeture du fichier

3 – Quelques Exercices

D’autres exercices sont disponibles sur ma playlist youtube.

3.1 – Récupérer les commentaires d’un programme python

Hypothèse : Les chaînes de caractère dans le programme python ne contiennent pas le caractère « # ».

Les étapes à suivre sont :

  1. Ouverture du fichier en mode lecture « r »
  2. Pour chaque ligne :
    1. Vérifier si la ligne contient le caractère « # » et récupérer son indice.
    2. Extraire la sous chaîne de caractère à partir du « # » jusqu’à la fin de la ligne.
    3. mettre cette sous chaîne dans une liste.
  3. Fermeture du fichier et renvoi de la liste.

Le code de la fonction python ressemble à ça :

def getComments(file):
f = open(file) # ouverture du fichier en mode lecture
ligne = f.readline()
res = []
while ligne != "": # parcours du contenu du fichier
pos = ligne.find("#") # recherche l'indice de '#'
if pos != -1:
res.append(ligne[pos:])
ligne = f.readline()
f.close() # fermeture du fichier
return res # renvoi du résultat

3.2 – Récupérer une ligne d’un fichier CSV

On dispose d’un fichier CSV qui contient un certain nombre de lignes et de colonnes. On voudrait récupérer une ligne particulière. Le programme doit accepter les indices positifs et négatifs. Si l’indice de la ligne se trouve en dehors de l’intervalle des lignes du fichier, le programme renvoie une liste vide.

Les étapes à suivre sont :

  1. Ouverture du fichier en mode lecture.
  2. Lecture du contenu du fichier dans un reader CSV.
  3. Vérification que l’indice de ligne appartient bien à l’intervalle.
  4. Fermeture du fichier et renvoie de la ligne

Le programme ressemble à :

def getLigne(file, n, sep=","):
f = open(file, 'r')
# lire le contenu du fichier
r = csv.reader(f, delimiter=sep, quoting=csv.QUOTE_NONNUMERIC)
liste = list(r)
f.close()
if (n < len(liste)) and (n >= -len(liste)):
res = liste[n]
else:
res = []
return res

3.3 – Récupérer une colonne d’un fichier CSV

Cet exercice ressemble beaucoup à l’exercice précédent. Il suffit juste d’inverser les lignes et les colonnes du fichier pour retrouver exactement la même situation.

Les étapes à suivre sont :

  1. Ouverture du fichier et récupération du contenu dans un reader CSV.
  2. Inversement des lignes et des colonnes
  3. Suite comme l’exercice précédent.

Le programme ressemble à ça :

def getColonne(file, n, sep=","):
f = open(file, 'r')
r = csv.reader(f, delimiter=sep, quoting=csv.QUOTE_NONNUMERIC)
lr = list(zip(*r)) # inverser les lignes et les colonnes

# vérifier que "n" est dans le bon intervalle
if (n < len(lr)) and (n >= -len(lr)):
r = list(lr[n])
else:
r = []
f.close()
return r

Il existe une autre manière de faire : L’idée est de lire le contenu du fichier, de parcourir le contenu ligne par ligne et extraire l’élément en question de chaque ligne. Le code ressemble à :

def getColonne(file, n, sep=","):
f = open(file, 'r')
r = csv.reader(f, delimiter=sep, quoting=csv.QUOTE_NONNUMERIC)
liste = list(r)
f.close()
res = []
if (n < len(liste[0])) and (n >= -len(liste[0])):
for ligne in liste:
res.append(ligne[n])
return res

4 – Conclusion

Dans cet artcile, on a vu comment manipuler (ouverture/lecture/écriture) d’un fichier texte en Python, et particulièrement un fichier CSV.

L’article a traité également quelques exercices liés aux fichiers CSV. Vous pouvez trouvez plus d’exercices sur les fichiers CSV en Python sur ma playlist youtube

Les conteneurs en Python : La classe range

1 – Introduction

Dans cet article de blog, on va découvrir la classe « range » et son utilisation en général. C’est une classe très utile surtout pour faire des boucles « for » avec un nombre d’itérations connu à l’avance, parcours de listes, de chaines de caractères, … etc.

2 – Utilisation

2.1 – Syntaxe

La classe range est utilisée quand on veut avoir un objet qui contient une suite de nombres entiers. Il existe trois syntaxes différentes pour créer un objet de type « range » :

>>> a1 = range(b)             # un seul paramètre
>>> a2 = range(a, b) # deux paramètres
>>> a3 = range(a, b, c) # trois paramètres
  • La syntaxe avec un seul paramètre crée un objet « a1 » de type « range » qui contient les éléments de « 0 » à « b-1 ». Si « b » est un nombre entier strictement positif, « a1 » contient exactement « b » éléments. Dans le cas contraire, « a1 » ne contient aucun élément. De manière générale :
range(b) --------> 0, 1, 2, ..., b-1
  • La deuxième syntaxe représente un forme un peu plus générique de la construction d’un objet « range ». En effet, avec cette syntaxe, on a la possibilité de préciser le début « a » de la suite de nombres. Dans ce cas, l’objet « a2 » contient les élément de « a » à « b-1 ». Si « b » est supérieur strictement à « a », l’objet « a2 » contient exactement « b-a » éléments, sinon (si « a » est supérieur ou égal à « b ») l’objet « a2 » ne contient aucun élément. De manière générale :
range(a, b) --------> a, a+1, a+2, ..., b-1
  • La troisième syntaxe est la forme la plus générique.  Dans ce cas là, trois paramètres sont à fournir qui correspondent respectivement au début de la suite de nombre, la borne supérieure, et le « pas ». Le « pas » est égal à la différence entre deux nombre successifs de cette suite de nombres. De manière générale :
range(a, b, c) --------> a, a+c, a+2*c, a+3*c, ..., a+k*c
tel que : a+k*c < b <= a+(k+1)*c

La figure suivante illustre le lien qui existe entre les trois syntaxes :

Figure 1: Equivalences entre les différentes syntaxes

2.2 – Cas d’utilisation

2.2.1 – Exemples

Généralement, on utilise « range » pour fixer le nombre d’itérations d’une boucle « for » (voir article sur les structures de contrôle).

for i in range(100):
print("Hello world!")

Le code précédent affiche cent (100) fois la phrase « Hello world! ».

2.3.2 – Méthodes d’un objet range

  • La méthode « index » :
    La méthode index prend un seul paramètre et retourne l’indice de ce paramètre dans l’objet. Si la valeur du paramètre n’appartient pas à l’objet « range », la méthode lève une exception de type « ValueError ».
>>> r = range(10)
>>> r.index(9)
9
>>> r.index(10)
Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
r.index(10)
ValueError: 10 is not in range
  • La méthode « count » :
    La méthode « count », quant à elle, permet de compter le nombre d’apparitions d’une valeur dans l’ibjet range.
>>> r = range(1, 10, 2)
>>> r.count(1)
1
>>> r.count(10)
0

2.3.3 – Attributs d’un objet range

La classe « range » définit trois attributs : « start », « stop », « step ». Ils correspondent respectivement à la valeur de début, la borne sup, et le pas. Les trois attributs sont en lecture seule; C’est-à-dire, qu’une fois l’objet range est créé, on ne peut plus modifier les valeurs de ces attributs.

>>> r = range(1, 100, 3)
>>> r.start
1
>>> r.stop
100
>>> r.step
3
>>> r.start = 6
Traceback (most recent call last):
File "<pyshell#10>", line 1, in <module>
r.start = 6
AttributeError: readonly attribute

Voici comment sont intialisés les trois attributs selon la syntaxe utilisée lors de la création de l’objet (Voir section 2.1) :

startstopstep
range(b)0b1
range(a, b)ab1
range(a, b, c)abc

2.3.4 – Quelques astuces

  • Vérifier qu’une valeur appartient à un objet range :

Pour vérifier qu’une valeur « v » appartient à une objet de type range « r », on peut utiliser l’opérateur « in » :

>>> r = range(0,100, 2)
>>> v = 50
>>> v in r
True
>>> 100 in r
False

Bien sûr, on peut également utiliser le méthode « count » qui retourne « 1 » si la valeur appartient bien à l’objet range, « 0 » sinon.

  • Construire une liste à partir des éléments range :

Comme on vu précécemment, un objet range ne stocke pas toutes les valeurs mais consèrve seulement les informations sur le début, fin, et le pas concernant la suite de nombres. Pour avoir la liste complète des nombres à partir des éléments d’un objet range :

>>> r = range(2, 10, 2)  
>>> l1 = list(r) # première syntaxe
>>> l2 = [*r] # deuxième syntaxe
>>> l1
[2, 4, 6, 8]
>>> l2
[2, 4, 6, 8]
  • Itérer sur un objet range :

Comme un objet de type « range » est un itérable, on peut créer un itérateur sur cet objet comme suit :

>>> r = range(0,10,1)
>>> it = iter(r)
>>> next(it)
0
>>> next(it)
1
>>> next(it)
2

3 – Conclusion

Dans cet article, nous avons présenté la classe python « range » de manière générale, les différente manières de créer des objets « range », et comment les utiliser.

Les gestion des erreurs en Python : Les exceptions

1 – Introduction

Pour qu’un programme soit complet, il doit tenir compte de tous les cas possibles, en particulier, quand il manipule des données saisies par l’utilisateur. En effet, un programme qui demande à un utilisateur de saisir un entier par exemple, doit vérifier que la saisie est bien un entier et non autre chose (chaîne de caractère ou flottant par exemple).

Un autre exemple, c’est quand un programme utilise des fonctions systèmes de manipulation de fichiers, de communication réseau, … etc. Il doit toujours vérifier que l’appel système a bien réussi et n’a pas généré d’erreur.

Le mécanisme utilisé pour la gestion des erreurs en Python est : Les « exceptions ».

2 – Gestion des erreurs

Dans un programme, quand on a une partie du code qui est suceptible de générer une erreur et donc de lever une exception, on place cette partie du code dans un bloc « try … except … ».

Voici la syntaxe dans le cas général :

try:
<code susceptible de générer une exception>
except:
<traitement de l'exception>
[
finally:
<code exécuté dans tous les cas>
]

Dans le code précédent, le bloc « try » commence à s’exécuter. Si tout se passe bien, le bloc « expect » sera ignoré et on passe directement au bloc « finally ».
Si jamais une erreur se produit, l’exécution du bloc « try » est stoppée. Une exception, dont le type dépend de la nature de l’erreur, est levée. Cette dernière va être traitée par le bloc « except » prévu à cet effet. Celui-ci. Enfin, le bloc « finally » s’exécute. Remarquez que le bloc « finally » s’exécute dans tous les cas.

Il arrive également où une même partie du code soit susceptible de générer plusieurs types d’exception dans ce cas, on a la possibilité de mettre un seul bloc « except », et donc on définie un traitement commun à toutes les exceptions comme montré dans le code précédent.

La deuxième solution serait de mettre plusieurs blocs « except », et donc de définir plusieurs traitements selon le type de l’exception :

try:
<code susceptible de générer des exceptions>
except <exception 1>:
<traitement exception 1>
except <exception 2>:
<traitement exception 2>
...
except <exception n>:
<traitement exception n>
[
finally:
<traitement final>
]

Python définie une multitude de classes qui pourraient être utilisées pour déclencher des exceptions. La liste exhaustive de la hiérarchie des exceptions se trouve ici.

3 – Créer de nouveaux types d’exceptions

Python offre la possibilité de créer de nouveaux types d’exception. Pour ce, il faut créer une classe qui hérite, de manière directe ou indirecte, de la classe « BaseException » (La classe « BaseEception » doit être une une classe ancêtre de la classe à définir).

L’exemple suivant montre la définition d’une classe « ErreurPersonnalisee » qui hérite de la classe « BaseException ». Elle définie deux attributs : « code » et « msg » :

class ErreurPersonnalisee (BaseException):
def __init__(self, msg, code):
super().__init__(msg)
self.msg = msg
self.code = code
def __str__(self):
return str(self.code) + " - " + str(self.msg) + "\n"

raise ErreurPersonnalisee("Informations sur l'erreur", 1)

L’affichage produit est :

Traceback (most recent call last):
    File "test.py", line 10, in <module>
       raise erreur("Informations sur l'erreur", 1)
__main__.erreur: 1 - Informations sur l'erreur

4 – Exemples

4.1 – Lecture d’un fichier json

Les erreurs qui peuvent se produisent quand on lit un fichier JSON sont :

  • Le fichier est inexistant (L’exception levée dans ce cas est : « FileNotFoundError »),
  • Le fichier existe mais le programme n’a pas de droit de lecture sur lui (l’exception est « PermissionError »),
  • Le contenu du fichier n’est pas au format JSON (« ValueError »).

Le code suivant détaille bien les différents cas :

import json

try:
fichier = open("fichier.json", "r")
contenu = json.load(fichier)
print(contenu)
fichier.close()
except FileNotFoundError:
print("Le fichier demandé n'existe pas")
except PermissionError:
print("Le fichier existe mais pas de droit de lecture")
except ValueError:
print("Le fichier n'est pas au format json"

4.2 – Saisie d’un nombre entier

Lorsqu’on demande à l’utilisateur de saisir un entier, les erreurs possibles qui peuvent se produire sont :

  • L’utilisateur saisie « <CTRL>+<D> » pour la fin de fichier (L’exception levée dans ce cas est « EOFError »).
  • L’utilisateur saisie une chaîne de caractères qui ne peut être convertie en entier (l’exception dans ce cas est « ValueError »).
while True:
try:
n = int(input("Donner un nombre"))
break
except EOFError:
print("L'utilisateur n'a rien saisi")
except ValueError:
print("La saisie n'a pas pu être convertie en entier")

5 – Déclencher une exception

On utilise le mot clé « raise » pour lever une exception dans un code donné :

raise <objet exception>

Il arrive aussi que, dans un bloc « except », on veuille remonter la même exception pour qu’elle soit traitée à un niveau supérieur (une des fonctions appelantes). Pour cela il suffit juste de d’utiliser le mot clé « raise » sans préciser l’exception.

try:
<code susceptible de générer des exceptions>
except:
<traitement partiel de l’exception>
raise
# l'exception est remontée au niveau supérieur pour qu'elle soit
# traitée.

6 – Conclusion

Dans cet article, on a vu le concept d’exception en python qui permet la gestion des erreurs. Python définie une multitude d’exception, chacune prévue pour une type d’erreur particulier.

Il est possible de capturer une exception avec le les bloc « try … except … finally … », dans le but de la traiter et éviter l’arrêt brusque de l’exécution du programme.

On a vu qu’il est possible de créer nos propres types d’exceptions adaptées à l’application à développer.

Les structures de contrôle en Python

1 – Introduction

Les instructions ne s’exécutent pas toujours séquentiellement. En effet, il arrive qu’on veuille choisir entre deux ou plusieurs traitements à exécuter selon la situation au moment de l’exécution, les autres traitements seront alors ignorés. 

Il arrive également qu’un traitement doive se faire plusieurs fois mais on ne veut pas dupliquer cette partie du code car cela rendrait le programme très volumineux.

Dans cet article on va détailler les différentes structures de contrôle du flux d’exécution d’un programme.

2 – Les structures de contrôle

Il existe principalement deux types de structures de contrôle dans un programme : la structure alternative et les boucles (traitement répétitif).

2.1 – Structure alternative : « if » … « elif » … « else »

La structure alternative permet de faire en sorte qu’un traitement ne s’exécute que si une condition est vérifiée. Autrement dit, après évaluation de la condition, si cette dernière a pour valeur « True », le traitement sera exécuté, dans le cas contraire, le traitement est ignoré et on passe à la suite.

La syntaxe d’une structure alternative est :

if <condition 1> :
<traitement 1>
[
elif <condition 2> :
<traitement 2>
elif <condition 3> :
<traitement 3>
...
elif <condition n> :
<traitement n>
]
[
else :
<traitement else>
]

Voici quelques remarques concernant le code précédent :

  • La ligne qui précède un bloc de traitement se termine par deux points « : ».
  • Toutes les instructions d’un bloc de traitement sont décalées par rapport à la ligne qui introduit le bloc : l’indentation en python est très importante car c’est elle qui détermine le début et la fin d’un bloc.
  • Ce qui est entre crochets « [] » est facultatif. En effet, un « if » ne possède pas forcément des « elif » et un « else ».
  • Pour ce qui est des blocs « elif », on peut en mettre autant qu’on veut :  Il n’y a pas de limite.
  • Il n’y a qu’un seul bloc « else » au plus, et s’il est présent, il est mis à la fin.
  • Il n’y a qu’un seul traitement <traitement i> au plus qui sera exécuté, et cela dépend des conditions <condition i>. En effet, la première condition <condition i> à être évaluée (en commençant su haut vers le bas) à « True », le bloc correspondant <traitement i> sera exécuté.
  • S’il n’y a aucune condition qui est vraie, le bloc du « else » (s’il existe) sera exécuté.

Voici un exemple plus concret :

n = input("Entrer un nombre : ")
if n % 2 == 0:
print("pair")
else:
print("impair")

Le code précédent demande à l’utilisateur de saisir un nombre puis vérifie sa parité. S’il y est pair, il affiche « pair », sinon il affiche « impair ».

2.2 – Boucle « for »

Une boucle permet de définir un traitement itératif, c-à-d que c’est un bloc qui s’exécute 0 (zéro) ou plusieurs fois. On utilise le mot clé « for » pour définir une boucle dont le nombre d’itérations est connu à l’avance.

En python, pour écrire une boucle « for », on a besoin d’une variable itérable (C’est-à-dire une variable contenant plusieurs éléments et qu’on peut itérer). L’indice de la boucle va prendre les différentes valeurs des éléments contenus dans l’itérable au fil des itérations.

La syntaxe d’une boucle « for » est la suivante :

for x in <iterable>:
<traitement for>

Le bloc <traitement for> sera exécuté autant de fois que d’éléments dans <itérable>. À chaque itération, la variable « x » prend la valeur d’un élément de <itérable>.

Voici un exemple plus concret d’une boucle « for » :

for i in range(10):
print(i)

C’est une boucle « for » qui fait 10 itérations. la variable « i » prend les valeurs de 0 à 9.

2.3 – Boucle « while »

Le deuxième type de boucles c’est la boucle « while ». Comme la boucle « for », la boucle « while » permet de définir un traitement répétitif qui s’exécute 0 à plusieurs fois. Par contre, le nombre d’itération n’est pas connu à l’avance mais plutôt dépend d’une condition. En effet, la boucle  « while » définie une condition qui sera évaluer avant chaque début d’itération : Si la condition est vraie, le bloc s’exécute une fois de plus, sinon, la boucle se termine et l’exécution du programme continue à partir de l’instruction qui suit la boucle.

Quand il s’agit de la boucle « while », on parle souvent de condition d’arrêt. Cette dernière représente la condition pour que le programme sort de la boucle et continue en séquence. La condition d’arrêt est tout simplement la négation de la condition de boucle.

Voici la syntaxe d’une boucle « while » :

while <condition>:
<traitement while>

Voici un code python qui affiche les nombre de 0 à 9 avec une boucle « while » :

a = 0
while
a < 10:
    print(a)

3 – Conclusion

Dans cet article, nous avons vu les trois manières possibles de rompre la séquence en Python. on utilise la structure alternative « if … elif … else » pour effectuer un traitement particulier selon la valeur d’une ou plusieurs conditions.

On utilise les boucles « for » et « while » pour les traitements répétitifs (des traitements qui s’exécutent 0 ou plusieurs fois). Si on connait à l’avance le nombre d’itérations c’est la boucle « for » qui faut utiliser, dans le cas contraire, la boucle « while » est à utiliser.

Python : affectation vs. copy vs. deepcopy

1 – Introduction

Quand on écrit un programme, on a souvent besoin de sauvegarder le contenu d’une variable dans une autre variable avant de la modifier. Ou alors, on construit une variable à partir du contenu d’une autre variable et changer uniquement les parties qui diffèrent. Là encore, on a besoin de copier une variable dans une autre.

En python, les choses ne sont pas aussi simples. En effet, il y a trois niveaux de copies qu’on va détailler dans cet article.

2 – Différents niveaux de copie

Avant d’expliquer en détail, il serait intéressant de présenter certaines fonctions Python qui nous seront utiles.

  • La fonction prédéfinie « id » :

En regardant l’aide donné par le shell python, et ce, en faisant « help(id) », après traduction on aura ça :

id(obj, /)
Renvoie l’identité d’un objet.


Ceci est garanti d’être unique parmi tous les objets existants simultanéments.
(CPython utilise l’adresse mémoire de l’objet.)

C’est important de comprendre ce que renvoie la fonction « id » car on va l’utiliser juste après pour mieux expliquer les différentes manières de copier des variables.

2.1 – Affectation

Pour comprendre de quoi il s’agit, voici un exemple de code python : 

>>> l1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> l2 = l1

La deuxième ligne de ce code crée un nouvelle référence au même objet liste contenant les éléments de 1 jusqu’à 10. Il est important de souligner qu’il n’y a qu’un seul objet liste référencé par deux références : « l1 » et « l2 ».

On peut le vérifier en affichant l’identité des objets référencés « l1 » et « l2 » en faisant :

>> print(id(l1), id(l2), sep="\n")
45511432
45511432

Le résultat varie d’une exécution à une autre mais ce qui est sûr, c’est que la valeur est la même pour les deux références.

Une autre manière de vérifier qu’il n’y a qu’un seul objet référencé par les deux références, c’est en modifiant le contenu de « l1 », par exemple, puis voir le contenu de « l2 » :

>>> l1[0] = 100
>>> print(l1, l2, sep="\n")
[100, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[100, 2, 3, 4, 5, 6, 7, 8, 9, 10]

On voit bien que c’est le même résultat pour les deux références, car, tout simplement, c’est le même objet qui a été modifié.

La figure suivante illustre bien ce qui se passe qu’on fait une affectation d’un objet à une variable (référence).

Figure 1 : Illustration d’un affectation

2.2 – copie simple (copy.copy)

Comme indiquer dans le titre, la fonction « copy » est définie dans le module python « copy ». Donc, pour pouvoir l’utiliser il faudra préalablement importer ce module en faisant :

import copy

Reprenons maintenant l’exemple précédent, et voyons ce qui se passe si on utilise la fonction « copy » au lieu d’une affectation :

>>> l1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> l2 = copy.copy(l1)
>>> print(id(l1), id(l2), sep="\n")
45511432
45833384

Remarquez ici, que les identités (donc les adresses) des objets référencés par « l1 » et « l2 » sont différentes, donc ça montre bien que ce sont deux objets distincts.

Prenant un autre exemple :

>>> l1 = [[1, 2, 3], [4, 5, 6]]
>>> l2 = copy.copy(l1)
>>> print(id(l1[0]), id(l2[0]), sep="\n")
45833640
45833640
>>> print(id(l1[1]), id(l2[1]), sep="\n")
45833544
45833544

Ceci montre que le premier élément (respectivement le deuxième élément) de chacune des deux listes « l1 » et « l2 » fait référence au même objet liste « [1, 2, 3] » (respectivement « [4, 5, 6] »). Si jamais on met à jour la valeur de « l1[0][0] », « l2[0][0] » sera modifié également.

La figure suivante illustre bien ce qui passe qu’on crée une copie en utilisant la fonction « copy.copy ».

Figure 2 : Illustration d’un copie simple avec « copy.copy »

La situation qu’on a vu précédemment peut être parfois gênante quand on a plusieurs niveaux de listes (c-à-d liste de listes de listes de …. ) ou d’objets de manière générale. En effet, il n’y a que le premier niveau (première liste) qui est dupliquer, mais les autres niveaux ne le sont pas.

Pour pallier à ce petit problème, il existe une manière pour faire une copie profonde qu’on va détailler juste après.

2.3 – copie profonde (dopy.deepcopy)

On a vu précédemment, « copy.deepcopy » permet de pallier à la limite de la copie simple en dupliquant tous les niveaux de listes (ou d’objets de manière générale). Ceci est fait de manière récursive dans le but de créer un nouvel objet identique au premier (Les deux ont les mêmes valeurs) mais qui sont totalement distincts.

Reprenons l’exemple précédent :

>>> l1 = [[1, 2, 3], [4, 5, 6]]
>>> l2 = copy.copy(l1)
>>> print(id(l1[0]), id(l2[0]), sep="\n")
45833448
33401128
>>> print(id(l1[1]), id(l2[1]), sep="\n")
45833384
45631656

Remarquez bien que les éléments des deux listes référencent des objets « liste » distincts. Car les valeurs de leurs identités sont différentes.

La figure suivante illustre bien ce qui se passe quand on fait une copie profonde avec « copy.deepcopy ».

Figure 3 : Illustration d’une copie profonde (deepcopy)

3 – Conclusion

Le but de cet est de bien montrer et expliquer la différence entre les trois niveaux de copies (affectation, copie simple, copie profonde). C’est très important de maîtriser ces notions pour éviter d’avoir des surprises à l’exécution d’un programme.

Commencer avec Tkinter

1 – Introduction

Tkinter est un module du langage Python, il est utilisé pour créer des interfaces graphiques. Il existe d’autres manières de faire des interfaces graphiques en Python (Utilisation de Qt par exemple), Cependant, Tkinter reste la bibliothèque la plus connue et la plus utilisée.

Le module « tkinter » est installé avec python, donc pas besoin de l’installer.

2 – Création d’une fenêtre basique

Une fenêtre graphique en « Tkinter » est une objet de type classe « Tk ». Cette classe est définie dans le module python qui s’appelle « tkinter ».

Il existe principalement trois manières pour créer une fenêtre graphique avec tkinter :

2.1 – Approche simple

J’ai appelé la première approche « approche simple » car elle nécessite pas des connaissance en programmation orientée objet python.

Avec cette approche tous les composants graphiques y compris notre fenêtre seront des variables « éparpillées » dans le programme.
Voici le code python pour la création d’une fenêtre basique :

#!/usr/bin/env python3
import tkinter as tk
Fenetre = tk.Tk()
# ... Ajouter des composants graphiques
Fenetre.mainloop()

Le code précédent affiche une fenêtre graphique vide : Aucun composant graphique dessus. Pour y ajouter des widgets, il faut les mettre dans la partie commentaire (avant dernière ligne).

Figure 1: Première fenêtre avec tkinter

2.2 – Approche par composition

La deuxième approche consiste à définir une classe, « Fenetre » par exemple, composée d’un objet de type classe « Tk ». La classe « Fenetre » possédera des attributs qui représenteront tous les composants graphiques de la fenêtre.

Le diagramme de classes UML qui correspond à cette approche est donné dans le figure suivante :

Figures 2 : Diagramme UML de composition

Voici un exemple de code qui permet de créer une fenêtre vide en utilisant l’approche par composition :

import tkinter as tk
class Fenetre:
def __init__(self):
f = tk.Tk()
# ... Ajouter des composants graphiques comme des attributs

f = Fenetre()
f.f.mainloop()

Le code précédent affiche une fenêtre vide comme celle de la figure 1.

2.3 – Approche par héritage

La troisième approche consiste à définir une classe, « Fenetre » par exemple, qui hérite de la classe « Tk ». Ainsi, la classe « Fenetre » va contenir tous ses composants graphiques comme des attributs.

La figure suivante montre le diagramme UML qui correspond à l’approche par héritage.

Figure 3 : Diagramme UML d’héritage

L’exemple ci-dessous permet de créer une fenêtre vide en utilisant l’approche par héritage :

import tkinter as tk
class Fenetre(tk.Tk):
  def __init__(self):
  tk.Tk.__init__(self)
# ... Ajouter des composants graphiques comme des attributs

f = Fenetre()
f.mainloop()

Le résultat est le même que celui montré dans figure 1.

3 – Conclusion

Parmi les trois approches présentées plus haut, il est toujours préféré d’opter pour la troisième approche surtout quand il s’agit de projets de grandes tailles. En effet, le code est mieux organisé et donc facile maintenir et à évoluer.

La première approche est adapté pour des programmes simples et/ou de petites tailles.

Premiers pas en Python

1 – Introduction

Si vous n’avez encore installer python, je vous conseille d’installer la dernière version qui, à ce jour, est python 3.8.1. Pour cela, vous pouvez suivre les étapes décrites dans un précédent article que vous pouvez trouver ici.

Vous savez sûrement que python est un langage interprété et non compilé. C’est-à-dire l’interpréteur python ne génère pas d’exécutable, il analyse le code ligne par ligne puis les exécute.

Autre particularité de Python, c’est le typage dynamique. En effet, pour utiliser une variable, on n’a pas besoin de déclarer son type. Au moment de la première affectation, python détecte le type de la valeur affectée. Puis, cette même variable peut changer de type si on lui affecte une valeur d’un autre type.

2 – Premier programme : « hello world »

Pour écrire un premier programme python sous linux, il faut créer un ficher texte qui s’appelle « hello-world.py » par exemple.

nano hello-world.py

Ensuite, il faut écrire le code suivant :

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

<ctrl>+O pour sauvegarder. <ctrl>+X pour quitter  

Pour exécuter le programme, il y a deux manière. La première technique consiste à rendre le fichier exécutable et le lancer directement.

chmod +x hello-world.py
./hello-world

La deuxième manière, consiste à donner le fichier comme argument au programme python3.8.

python3.8 hello-world.py

3 – Interagir avec le shell python

Le site officiel met à votre disposition un shell python interactif pour découvrir le langage de programmation python. Mais pour ceux d’entre vous qui l’avez installé sur vos machines, Vous pouvez lancer le shell python en ouvrant un terminal linux, puis taper la commande :

python3.8

Si vous l’avez bien installé, normalement vous aurez quelque chose qui ressemble à ça :

Python 3.8.1 (default, Dec 21 2019, 19:58:48)
[GCC 8.3.0] on linux
Type « help », « copyright », « credits » or « license » for more information.
>>>

Les trois chevrons « >>> » indique que le shell python attend que vous entriez une instruction qu’il va exécuter pour vous.
Vous pouvez par exemple taper :

>>> print('hello-world')

La fonction « print » affiche ce qu’on lui donne en argument. C’est-à-dire, ici, elle affiche « hello-world ».

Autre instruction que vous pouvez essayer :

>>> a = 1
>>> b = 'Bonjour'
>>> pi = 3.14
>>> 1 + 1
2
>>> 9 * 9
81
>>>

Dans l’exemple précédent, on a défini des variables « a », « b », et « pi » qui valent respectivement « 1 », « ‘Bonjour' », et « 3.14 ».
On a utilisé également le shell python comme une calculatrice pour calculer la somme « 1+1 » et le produit « 9*9 ».

Pour quitter le shell, vous avez juste à écrire l’une de ces deux instructions :

>>> quit()

ou

>>> exit()

Et là vous revenez au terminal linux.

Installation Python 3.8 sous Debian / Ubuntu

1 – Introduction

Python est un langage de script orienté objet développé en début des années 1990. La version 3.8 est sortie le 14 octobre 2019. Une mise-à-jour Python 3.8.1 est sortie en décembre 2019.

Python3.8 vient avec quelques nouveautés par rapport à la version précédente 3.7. pour plus de détails, je vous conseille la documentation du site officiel ici.

Par ailleurs, « Python Software Foundation » annonce qu’elle mettra fin au support de la version Python 2 à partir du 1er janvier 2020. Par conséquent, il ne reste que quelques jours pour passer à la Python 3.

2 – Préparation

Avant de commencer l’installation de python 3.8, il est nécessaire d’installer certains paquets qui seront utilisés lors de l’installation de Python. Sur l’invite de commande taper les commandes :

sudo apt-get install build-essential checkinstall

sudo apt-get install libreadline-gplv2-dev libncursesw5-dev libssl-dev libsqlite3-dev tk-dev libgdbm-dev libc6-dev libbz2-dev libffi-dev zlib1g-dev

3 – Téléchargement

Il vous faudra télécharger le code source de python. Je vous conseille de prendre la version la plus récente à ce jour c-à-d python 3.8.1. Pour celà il faut exécuter la commande :

cd /opt
wget https://www.python.org/ftp/python/3.8.1/Python-3.8.1.tgz

Ces commandes téléchargent le ficher « .tgz » contenant le code source python dans le répertoire « /opt »

4 – Installation

4.1 – Extraction

Afin d’extraire les fichier qui se trouve dans le ficher archive, il faut exécuter la commande :

sudo tar xzf Python-3.8.1.tgz
cd Python-3.8.1

4.2 – Installation

sudo ./configure --enable-optimizations
sudo make altinstall

5 – Vérification

Taper la commande suivante sur un terminal :

python3.8 -V

Si tout s’est bien passé, le résultat qui s’affiche est :

Python 3.8.1

Le chemin complet de l’exécutable est « /usr/bin/python3.8 »

Maintenant que l’installation s’est bien terminée, vous pouvez supprimer le fichier « .tgz » que vous avez téléchargé.

rm -f /opt/Python3.8.1.tgz