Lancer plusieurs processus sans utiliser de threads (Labortablo – 3ème partie)

Mon ordinateur est équipé de deux écrans. Par conséquent, j’ai fait en sorte que mon interface de bureau moulé à la louche, prenne en compte ce cas de figure. Dans un premier temps, j’ai utilisé le module multiprocessing. Cela me semblait être la solution idéale pour lancer simultanément un process sur chaque écran.

Seulement, il y a un léger problème. threading et multiprocessing rentrent en conflit avec des interfaces graphiques telles que tkinter. Le résultat final présente des signes d’instabilité qui ne sont pas du meilleur effet. En théorie, on peut utiliser multiprocessing avec tkinter mais toutes les interactions avec l’interface graphique doivent se faire par un seul et unique thread. Le module multiprocessing peut traiter des données et faire des calculs dans plusieurs threads, mais il ne peut pas « multithreader » tkinter! Les deux processus doivent être séparés.

Donc, dans un souci d’amélioration continue, je me suis mis en quête d’une solution alternative, et je pense avoir codé quelque chose qui comble mes attentes en terme de stabilité. Alors comment ai-je procédé? En fait, je suis parti de l’idée toute simple que pour deux écrans, il faut avoir non pas deux threads d’un même programme mais deux programmes! Et je voulais que tout le processus se déroule de manière automatique. Pas question pour l’usager, d’ouvrir un fichier de configuration pour en modifier le contenu. L’objectif était vraiment de coder quelque chose qui s’installe tout seul et qui vit sa vie sans intervention de maintenance. Mais comment faire pour donner l’illusion de lancer et d’arrêter deux threads?

  1. Situation initiale

Voici la configuration initiale avec multiprocessing.

2. Suppression de multiprocessing, modification du code et explications détaillées

Au démarrage de l’ordinateur, le programme se lance grâce au fichier start.py dont le chemin se trouve dans ~/.config/openbox/autostart.

Examinons, si vous le voulez bien, ce script. La première opération qu’il exécute, consiste à détruire tous les fichiers et répertoires antérieurs qui ne correspondent pas à la configuration par défaut (en monomoniteur, si vous me permettez ce néologisme). Il est nécessaire de partir sur une base propre:

Version standard monomoniteur

Pourquoi détruire la configuration multi-écrans à chaque démarrage? Eh bien parce que l’utilisateur peut très bien décider, un beau matin, de suppprimer un écran ou d’en rajouter un troisième. Je ne suis pas dans sa tête et je ne connais pas ses intentions.

Ensuite, vient le moment de déterminer le nombre de moniteurs. Plutôt que de compter avec ses doigts, confions cette tâche à la commande suivante:

xrandr --listactivemonitors

Résultat:

Monitors: 4
 0: +*HDMI-1 1920/698x1080/392+1920+0  HDMI-1
 1: +VGA-1 1920/477x1080/268+0+0  VGA-1
 2: etc...
 3: etc...

Je crois que c’est clair… Maintenant, le programme va créer autant de répertoires qu’il y a de moniteurs supplémentaires, et il va copier le contenu de labortablo dans chacun d’entre eux. Si vous avez quatre écrans, il va donc créer en sus de labortablo (qui est en quelque sorte le « répertoire zéro« ):

  • labortablo_1
  • labortablo_2
  • labortablo_3

Nous nous retrouvons donc avec cette configuration:

Version quadrimoniteur

Voici la ligne de code qui effectue cette opération:

if i > 0:
    shutil.copytree(
        f'{self.cwd}/labortablo',
        f'{self.cwd}/labortablo_{i}'
        )

De cette manière, si je veux apporter des modifications au code avant de le pousser dans mon dépôt Git, il me suffit de modifier labortablo, le « répertoire zéro ». Je sais qu’au prochain démarrage, les modifications seront prises en compte pour tous les autres programmes.

Ensuite le script start.py se charge de traiter les informations relatives aux écrans. Puis, il créé un fichier coords.txt pour chaque interface de bureau. Ce fichier est construit de telle sorte qu’il puisse se transformer très facilement en dictionnaire. Je trouve que cette syntaxe facilite bien la vie.

w:1920   # largeur
h:1080   # hauteur
x:0      # abscisse
y:0      # ordonnée

Ainsi, je peux passer rapidement ces paramètres aux classes qui en ont besoin. Tous les éléments graphiques vont récupérer ces informations pour configurer l’interface de bureau sur chaque écran.

args = dict()
with open('coords.txt', 'r') as rfile:
    for line in rfile.readlines():
        args[line.split(':')[0]] = int(line.strip().split(':')[1])

Le moment est venu de créer le fichier start.sh. Il va contenir tous les programmes à lancer et il sera détruit au prochain démarrage. Donc, dans le cas de figure ou il y a quatre écrans, voici à quoi ressemble ce script:

#!/bin/bash

/home/miamondo/chemin/vers/labortablo/taskbar.py &
/home/miamondo/chemin/vers/labortablo/categories.py &
/home/miamondo/chemin/vers/labortablo_1/taskbar.py &
/home/miamondo/chemin/vers/labortablo_1/categories.py &
/home/miamondo/chemin/vers/labortablo_2/taskbar.py &
/home/miamondo/chemin/vers/labortablo_2/categories.py &
/home/miamondo/chemin/vers/labortablo_3/taskbar.py &
/home/miamondo/chemin/vers/labortablo_3/categories.py &

Alors, vous allez me dire qu’il ne s’agit pas d’un script Python. Par conséquent, je ne devrais pas affirmer que Labortablo est une interface de bureau entièrment codée en Python. C’est vrai. Je le reconnais. Mais je n’ai pas d’autres choix si je veux lancer mon métaprogramme. On peut très bien lancer des fichiers *.py à partir d’un fichier Python mais j’ai ouï-dire que ce genre d’hérésie conduisait tout droit au bûcher.

Avec Python, on lance des modules, pas des fichiers, et à ce stade des opérations, je ne peux pas car il faudrait que j’utilise threading ou multiprocessing. C’est le python qui se mord la queue!

Enfin, dernière opération, je rend le fichier start.sh exécutable. Il faut le faire à chaque fois car il est détruit à chaque démarrage. Puis start.sh est lancé par start.py.

Conclusion

J’ai bien conscience du fait que cette manière de procéder est loin d’être académique mais d’une part, je m’en fiche un peu, et d’autre part ça tourne comme une horloge. J’utilise cette interface de bureau tous les jours, non pas dans une machine virtuelle mais sur mon PC principal. Il faut encore que je lui rajoute la gestion des catégories et des applications (ajouts/suppressions). Il faut aussi que je fasse le point sur tout ce qu’il y a à installer pour faire tourner le programme (xrandr, wmctrl, xprop etc…)

Voilà… Je n’oublie pas de vous souhaiter un Joyeux Nowouel et une bonne vaccination à toutes et à tous.

Labortablo, une interface de bureau codée en Python sur une base Archlinux/Openbox (2ème partie)

La barre de tâches

Sommaire

1. La boucle

2. Récupérer et convertir les icônes avec xprop

3. Iconifier et déiconifier les fenêtres


Voici le deuxième article sur mon interface de bureau moulé à la louche. Il traite de la barre des tâches. Le processus fonctionne de manière tout à fait satisfaisante mais il m’a vraiment donné du fil à retordre. Cette zone qui se trouve en bas de l’écran, affiche:

Lire la suite de « Labortablo, une interface de bureau codée en Python sur une base Archlinux/Openbox (2ème partie) »

Labortablo, une interface de bureau codée en Python sur une base Archlinux/Openbox (1ère partie)

Dans cet article, je vais vous présenter un projet personnel qui certes, m’a pris un peu de temps, mais que j’ai réussi à mener jusqu’à son terme, après plusieurs échecs. Il est construit sur une base Archlinux et Openbox. Pour le coder, j’ai utilisé un bien bel outil qui fera sans doute l’objet d’un prochain article. Il s’agit de pycharm, en version Community gratuite et open source. C’est un vrai plaisir de coder avec cet environnement de développement intégré, dont le thème Material se marie à merveille avec le design de mon projet.

Lire la suite de « Labortablo, une interface de bureau codée en Python sur une base Archlinux/Openbox (1ère partie) »

Chapitre 12 – Les tuples

Un tuple est un ensemble d’éléments comparable aux listes mais qui, une fois déclaré, ne peut plus être modifié. Il s’agit donc d’une séquence immuable d’objets indicés qui peuvent être des nombres entiers ou décimaux, des chaînes de caractères, des listes, des tuples etc…

  1. Syntaxe et déclaration

D’un point de vue syntaxique, un tuple est un ensemble d’éléments séparés par des virgules. Cet ensemble d’éléments est entouré de parenthèses mais ce n’est pas une obligation. Cela permet toutefois d’améliorer la lisibilité du code.

Déclarons un tuple :

tuple_1 = (8, "Solange", 5.3)

J’aurais très bien pu écrire :

tuple_1 = 8, "Solange", 5.3

Cela revient au même sauf que c’est moins lisible.

print(type(tuple_1))

<class ‘tuple’>

Sans le savoir, nous avons déjà rencontré des tuples, notamment lorsque nous avons fait des affectations multiples :

a, b = 5, 6

Cette instruction d’affectation correspond à :

(a, b) = (5, 6)

Si nous souhaitons déclarer un tuple ne contenant qu’une seule valeur, il faut absolument que cette dernière soit suivie d’une virgule. En outre, il est vivement conseillé de l’inclure entre parenthèses.

tuple_2 = (9,)

<class ‘tuple’>

Si nous oublions la virgule (et malgré la présence de parenthèses), le type de la variable ne sera pas un tuple :

tuple_2 = (9)

<class ‘int’>

2. Utilité d’un tuple par rapport à une liste

Comme nous l’avons vu plus haut, il permet de faire des affectations multiples. Le tuple permet également de renvoyer plusieurs valeurs lors d’un appel de fonction.

def informations_personne(prenom, nom, age) :
    "Traitement des informations transmises"

    prenom = prenom.capitalize() # Première lettre en majuscule.
    nom = nom.upper() # remplace toutes les minuscules en majuscules
    age = int(age)

    return prenom, nom, age # Tuple sans parenthèses

p = input("Entrez votre prénom: ")
n = input("Entrez votre nom: ")
a = input("Entrez votre âge: ")

prenom, nom, age  =  informations_personne(p, n, a) # Affectation multiple
print("\nPrénom: {}\nNom: {}\nÂge: {}".format(prenom, nom, age))
Entrez votre prénom: Cannabisse
Entrez votre nom: Fumète 
Entrez votre âge: 92

Prénom: Cannabisse
Nom: Fumète 
Âge: 92

3. Un tuple est « protégé en écriture ».

Vous allez me dire que dans le code ci-dessus, nous pouvons obtenir le même résultat en renvoyant une liste (return [prenom, nom, age]). Mais l’avantage d’un tuple, c’est qu’il s’agit d’une séquence non modifiable (immuable) donc protégée en écriture. Nous sommes sûrs que les données transmises par la fonction input() ne seront pas modifiées en cours d’exécution par un autre programme. Dans le code ci-dessus, je n’ai aucun moyen de modifier les informations transmises.

Par contre, dans le code ci-dessous où je renvoie une liste, je peux modifier les données transmises et rajeunir Mamie Alphonsine!

def informations_personne(prenom, nom, age) :
    "Traitement des informations transmises"

    prenom = prenom.capitalize() # Première lettre en majuscule.
    nom = nom.upper() # remplace toutes les minuscules en majuscules
    age = int(age)

    return [prenom, nom, age] # liste

p = input("Entrez votre prénom: ")
n = input("Entrez votre nom: ")
a = input("Entrez votre âge: ")

identification = informations_personne(p, n, a) #Liste
identification[2] = '45' # Je modifie l'âge
print("\nPrénom: {}\nNom: {}\nÂge: {}".format(identification[0], identification[1], identification[2]))
Entrez votre prénom: Alphonsine
Entrez votre nom: Senticule 
Entrez votre âge: 87

Prénom: Alphonsine
Nom: Senticule 
Âge: 45

4. Un tuple est moins gourmand en ressources système qu’une liste.

Il a besoin de moins de mémoire et il s’exécute plus rapidement.

5. Opérations sur les tuples

Il n’existe pas de méthodes associées aux tuples. Les tuples sont des séquences non modifiables donc il n’est pas possible d’utiliser les méthodes remove() ou append() par exemple. Il n’est pas possible également d’utiliser l’opérateur [] pour insérer ou remplacer un élément :

tuple_1 = (5, 2, 25, 56)
tuple_1[2] = 23

TypeError: ‘tuple’ object does not support item assignment.

6.L’instruction in

Il est possible d’utiliser l’instruction in pour faire un test d’appartenance

tuple_1 = (5, 2, 25, 56)
print(2 in tuple_1)

Résultat: True

7. La fonction intégrée len()

Il est également possible d’utiliser la fonction intégrée len() pour déterminer la longueur d’un tuple.

tuple_1 = (5, 2, 25, 56)
print(len(tuple_1))

Résultat : 4

8. La boucle for et la compréhension de liste

Tout comme les chaînes de caractères ou les listes, les tuples peuvent être parcourus par une boucle for ou une compréhension de liste. Dans ce dernier cas, le résultat obtenu est toujours une liste.

9. Concaténation et multiplication

Nous pouvons créer un nouveau tuple par concaténation ou par multiplication

tuple_1 = (5, 2, 25, 56)
tuple_2 = ("Jacky", "Hervé")
tuple_3 = tuple_1 + tuple_2
print(tuple_3)

Résultat: (5, 2, 25, 56, « Jacky », « Hervé »)

tuple_prenoms = ("Jacky", "Hervé")
tuple_prenoms = tuple_prenoms * 3
print(tuple_prenoms)

Résultat: (‘Jacky’, ‘Hervé’, ‘Jacky’, ‘Hervé’, ‘Jacky’, ‘Hervé’)

Dans ce dernier exemple, certains vont me dire: « Mais tu nous as dit qu’un tuple n’était pas modifiable et tu viens juste de modifier tuple_prenoms! »

Faux! Je n’ai appliqué aucune méthode de modification sur tuple_prenoms. J’ai seulement écrasé la valeur contenue dans la variable qui porte le nom de tuple_prenoms en lui affectant une nouvelle valeur.

Conclusion

Les tuples sont des séquences non modifiables qui s’avèrent bien utiles pour protéger en écriture des données transmises. En outre, ils sont moins gourmands en ressource système et sont traités plus rapidement.

Puisqu’il est immuable, il est possible d’utiliser un tuple comme clé de dictionnaire mais à la seule condition que le tuple ne contienne que des éléments non modifiables! Du reste, ce sera l’objet du prochain chapitre.

Chapitre 11 – La compréhension de liste

La compréhension de liste est une expression qui permet de construire une liste à partir de tout autre type itérable (liste, tuple, chaîne de caractères…). Le résultat obtenu est toujours une liste.

1. Une compréhension de liste toute simple

Un exemple valant mieux qu’un long discours, nous allons créer une liste de nombres entiers :

liste_initiale = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]

À l’aide d’une boucle for et de la méthode append(), nous allons ajouter à une nouvelle liste baptisée liste_2, chaque élément de liste_initiale multiplié par deux.

liste_initiale = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
liste_2 = []
for n in liste_initiale:
    liste_2.append(n*2)
print(liste_2)

Résultat: [0, 2, 4, 6, 8, 10 ,12, 14, 16, 18, 20, 22, 24, 26, 28, 30]

J’ai donc utilisé une boucle for pour écrire ce petit bout de code de cinq lignes. Mais j’aurais tout aussi bien pu utiliser une compréhension de liste pour obtenir le même résultat. Voici la syntaxe de cette dernière :

liste_initiale = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
liste_2 = [n*2 for n in liste_initiale]
print(liste_2)

Résultat: [0, 2, 4, 6, 8, 10 ,12, 14, 16, 18, 20, 22, 24, 26, 28, 30]

Que remarque-t-on? Le code est plus concis puisqu’il tient sur trois lignes au lieu de cinq. Vous allez me dire que la différence est minime et dans cet exemple tout simple, je dois reconnaître que vous avez raison! Mais attendez de voir la suite…

2. Une compréhension de liste avec une condition

Je souhaite  créer une nouvelle liste de nombres pairs multipliés par 2 à partir de liste_initiale qui contient, je vous le rappelle des nombres pairs et impairs. Il va donc falloir que j’introduise une condition pour ignorer les nombres impairs. Comparons une nouvelle fois le code écrit avec une boucle for et celui écrit avec une compréhension de liste.

Avec une boucle for
liste_initiale = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
liste_pair = []
for n in liste_initiale:
    if n % 2 == 0:
        liste_pair.append(n*2)
print(liste_pair)

Résultat: [0, 4, 8,  12, 16, 20, 24, 28]

Avec une compréhension de liste

J’introduis la condition à la fin de la compréhension de liste. Au lieu d’avoir six lignes de code, je n’en ai plus que trois.

liste_initiale = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
liste_pair = [n*2 for n in liste_initiale if n % 2 == 0]
print(liste_pair)

Résultat: [0, 4, 8,  12, 16, 20, 24, 28]

3. Une compréhension de liste avec une expression conditionnelle

Encore plus fort, je souhaite créer une nouvelle liste avec les éléments de liste_initiale. Les chiffres pairs seront multipliés par deux, les chiffres impairs par trois. Il va donc falloir que j’introduise une condition if… else.

Avec une boucle for
liste_initiale = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
nouvelle_liste = []
for n in liste_initiale:
    if n % 2 == 0:
        nouvelle_liste.append(n*2)
    else:
        nouvelle_liste.append(n*3)
print(nouvelle_liste)

Résultat : [0, 3, 4, 9, 8, 15, 12, 21, 16, 27, 20, 33, 24, 39, 28, 45]

Avec une compréhension de liste

Ça se complique un peu car il faut utiliser une expression conditionnelle qui évalue si la condition est vraie (True) ou fausse (False). Mais le résultat est au rendez-vous puisqu’au lieu d’avoir huit lignes de code, je n’en ai que trois!

liste_initiale = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
nouvelle_liste = [n*2 if n % 2 == 0 else n*3 for n in liste_initiale]
print(nouvelle_liste)
 4. Une compréhension de liste avec une liste de listes

Il est possible de faire une compréhension de liste avec une liste de listes. Pour s’y retrouver dans l’imbrication des instructions, le plus simple est de comparer la syntaxe d’une boucle for avec celle d’une compréhension de liste.

Avec une boucle for
liste_initiale = [[0, 'a'], [2, 'b'], [3, 'c']]
nouvelle_liste = []
for sous_liste in liste_initiale :
    for n in sous_liste :
        nouvelle_liste.append(n*2)
print(nouvelle_liste)

Résultat: [0, ‘aa’, 4, ‘bb’, 6, ‘cc’]

Avec une compréhension de liste
liste_initiale = [[0, 'a'], [2, 'b'], [3, 'c']]
nouvelle_liste = [n*2 for sous_liste in liste_initiale for n in sous_liste]
print(nouvelle_liste)

Résultat: [0, ‘aa’, 4, ‘bb’, 6, ‘cc’]

Avec une expression conditionnelle

Par exemple, je veux multiplier les nombres entiers par deux et les strings par trois.

liste_initiale = [[0, 'a'], [2, 'b'], [3, 'c']]
nouvelle_liste = [n*2 if type(n) == int else n*3 for sous_liste in liste_initiale for n in sous_liste]
print(nouvelle_liste)

Résultat: [0, ‘aaa’, 4, ‘bbb’, 6, ‘ccc’]

Dans ce dernier exemple, si j’avais fait usage d’une boucle for, j’aurais écrit neuf lignes de code au lieu de trois!

liste_initiale = [[0, 'a'], [2, 'b'], [3, 'c']]
nouvelle_liste = []
for sous_liste in liste_initiale:
    for n in sous_liste:
        if type(n) == int:
            nouvelle_liste.append(n*2)
        else:
            nouvelle_liste.append(n*3)
print(nouvelle_liste)

Cela dit, ce n’est pas non plus le concours de celui qui écrira le plus d’instructions sur la même ligne de code. Il faut que ça reste déchiffrable.

5. Les compréhensions de liste avec les autres types itérables

Comme je le disais en introduction, les compréhensions de liste fonctionnent avec les autres types itérables mais le résultat obtenu est toujours une liste. Par exemple, avec une chaîne de caractères :

prenom = "Gustave"
liste_lettres = [lettre for lettre in prenom]
print(liste_lettres)

Résultat: [‘G’, ‘u’, ‘s’, ‘t’, ‘a’, ‘v’, ‘e’]

Conclusion

La compréhension de liste est une méthode puissante qui a remplacé les anciennes fonctions map() et filter() dont l’usage est aujourd’hui déconseillé. Bien utilisée, la compréhension de liste rend le code plus concis, plus élégant et plus facile à comprendre. Voici la page consacrée aux compréhensions de liste dans la documentation officielle du langage Python.

Il existe également une compréhension de dictionnaire et d’ensemble que nous aborderons dans le chapitre consacré à ces deux structures de données.

Chapitre 9 – Les listes

Une liste est un ensemble d’éléments séparés par des virgules et entourés de crochets. Ces éléments peuvent être de n’importe quel type: str(), int(), float(). Il existe même des listes de listes! Contrairement aux chaînes de caractères, il est possible de modifier une liste grâce au slicing et à différentes méthodes que je vais vous présenter dans ce chapitre.

pythontutor

Déclarer une liste

Pour déclarer une liste vide, il existe deux syntaxes différentes:

liste_1 = []
liste_2 = list()
print(liste_1, liste_2)

Résultat : [] []

Dans le premier cas, je crée une liste vide. Dans le deuxième cas, je crée un objet de la classe list(). Mais vous pouvez tout à fait déclarer une liste et lui affecter directement des valeurs :

liste_3 = [8, 6.9, "Bertrance"]
print(liste_3)

Résultat : [8,  6.9, ‘Bertrance’] 

La deuxième syntaxe, list(), est utilisée pour convertir par exemple un tuple en liste. Nous verrons les tuples dans un prochain chapitre mais sachez déjà qu’un tuple est un ensemble d’éléments immuable séparés par des virgules et entourés de parenthèses. En fait, un tuple est une liste qu’on ne peut pas modifier. La seule solution est donc de transformer le tuple en liste.

tuple_prenoms = ("Jobriel", "Delphonse", "Bertrance")
liste_prenoms = list(tuple_prenoms)
print(liste_prenoms)

Résultat: [‘Jobriel’, ‘Delphonse’, ‘Bertrance’]

La méthode append( )

Grâce à la méthode append(), vous pouvez ajouter des éléments à une liste. Ils seront indexés à la fin de celle-ci.

prenoms = ["Jobriel", "Delphonse", "Bertrance"]
prenoms.append("Gustule")
print(prenoms)

Résultat : [‘Jobriel’, ‘Delphonse’, ‘Bertrance’, ‘Gustule’]

La fonction intégrée len( )

La fonction intégrée len() retourne un entier qui correspond au nombre d’éléments contenus dans la liste passée en argument.

print(len(prenoms))

Résultat : 4

Accéder à un élément d’une liste grâce à son indice

La variable prenoms contient quatre éléments indicés à partir de zéro, c’est-à-dire que l’indice du quatrième élément est 3. Avec l’indice, il est possible d’accéder à un élément précis de la liste. La syntaxe est la suivante. Je place l’indice de l’élément recherché, entre crochets.

personne = prenoms[2] 
print(personne)

Résultat : ‘Bertrance’.

Cela correspond au troisième élément de la liste, c’est-à-dire celui qui porte l’indice 2.

La méthode insert( )

La méthode insert(indice, nouvel_element) permet d’intercaler un nouvel élément dans une liste:

prenoms.insert(1, "Amandale") 
print(prenoms)

Résultat : [‘Jobriel’, ‘Amandale’, ‘Delphonse’, ‘Bertrance’, ‘Gustule’]

Cette méthode intercale l’élément passé en argument à l’indice passé en argument. « Amandale » se retrouve donc à l’indice 1 et les valeurs qui suivent, changent d’indice (i devient i + 1). Ainsi, « Delphonse » qui était à l’indice 1, se retrouve à l’indice 2.

La méthode extend( )

La méthode extend() permet de concaténer deux listes :

jours = ["Lundi", "mardi", "mercredi"]
jours2 = ["Jeudi", "Vendredi"]
jours.extend(jours2)
print(jours)

Résultat : [« Lundi », « Mardi », « Mercredi », « Jeudi », « Vendredi »]

Attention! Il ne faut pas confondre extend() et append()! La méthode extend() effectue une concaténation de listes tandis qu’append() rajoute l’élément passé en argument, à la fin de la liste sur laquelle la méthode est appliquée. Dans l’exemple précédent, voici ce que j’aurais obtenu si j’avais utilisé la méthode append():

jours = ["Lundi", "mardi", "mercredi"]
jours2 = ["Jeudi", "Vendredi"]
jours.append(jours2)
print(jours)

Résultat :  [« Lundi », « mardi », « mercredi », [« Jeudi », « Vendredi »]]

Je n’ai pas concaténé deux  listes mais j’ai rajouté la liste jours2 aux éléments de la liste jours. Cette dernière ne contient donc pas 5 éléments (comme dans le premier exemple) mais quatre éléments : Trois chaînes de caractères et une liste! Pour vous en assurer, il vous suffit d’utiliser la fonction intégrée len().

Accéder à un élément d’une liste qui se trouve elle-même dans une autre liste

jours est donc une liste qui contient une autre liste plus petite. Nous avons vu qu’il était possible d’accéder à un élément en utilisant l’indice entouré de crochets. Mais comment faire pour accéder à « Vendredi » qui est un élément de la petite liste qui se trouve en dernière position de la liste principale? C’est très simple! Il suffit d’utiliser cette syntaxe :

jours = ["Lundi", "mardi", "mercredi", ["Jeudi", "Vendredi"]]
element = jours[3][1]
print(element)

Résultat: Vendredi. Cette valeur correspond donc à jours[3][1].

  • 3 est l’indice de la petite liste dans la liste principale.
  • 1 est l’indice de « Vendredi » dans la petite liste.

Utiliser le slicing pour modifier une liste

Il est tout à fait possible d’utiliser les crochets et les indices pour insérer ou supprimer des éléments dans une liste. Cette technique s’appelle le slicing. Cela se traduit par découpe en tranches. C’est clairement plus délicat à manipuler que les méthodes mais c’est très efficace et d’une grande souplesse. Par exemple, au lieu d’appliquer la méthode list.insert(), on peut utiliser la syntaxe list[i:i].

ours = ["Lundi", "Mardi", "Jeudi"]
jours[2:2] = ["Mercredi"]
print(jours)

Résultat: [‘Lundi’, ‘Mardi’, ‘Mercredi’, ‘Jeudi’]

Attention! L’élément à insérer doit forcément être une liste. Vous pouvez insérer plusieurs éléments mais si vous n’en insérez qu’un seul, comme dans l’exemple ci-dessus, il faut le présenter entre crochets et par conséquent le convertir en une liste d’un seul élément.

Si vous oubliez les crochets, Python va insérer chaque caractère de la chaîne « Mercredi » en tant qu’élément. Ce n’est sans doute pas le but recherché!

[‘Lundi’, ‘Mardi’, ‘M’, ‘e’, ‘r’, ‘c’, ‘r’, ‘e’, ‘d’, ‘i’, ‘Jeudi’]

Pour faire un print() d’une partie des éléments d’une liste, par exemple les éléments indicés 1 et 2 dans notre liste prenoms, le code sera celui-ci :

prenoms = ['Jobriel', 'Amandale', 'Delphonse', 'Bertrance', 'Gustule']
print(prenoms[1:3])

Résultat: [‘Amandale’, ‘Delphonse’]

Dans cette tranche, l’élément indicé 1 est pris en compte tout comme l’élément indicé 2 mais pas l’élément indicé 3! Voici un schéma qui permet de mieux comprendre comment fonctionne le slicing.

['Jobriel', 'Amandale', 'Delphonse', 'Bertrance', 'Gustule']
|          |           |            |            |         |
0          1           2            3            4         5 

La tranche [1:3] correspond à « Amandale » et « Delphonse ». La tranche [3:4] correspond au prénom « Bertrance ». Quant au dernier chiffre (5), il correspond au len(), c’est-à-dire au nombre d’éléments que contient la liste.

Si j’insère « Hervine » dans la tranche [1:3], alors ce prénom remplace « Amandale » et « Delphonse ».

prenoms = ['Jobriel', 'Amandale', 'Delphonse', 'Bertrance', 'Gustule']
prenoms[1:3] = ["Hervine"]  # Ne pas oublier les crochets!
print(prenoms)

Résultat : [‘Jobriel’, ‘Hervine’, ‘Bertrance’, ‘Gustule’]

Autres exemples de slicing

prenoms[:][‘Jobriel’, ‘Amandale’, ‘Delphonse’, ‘Bertrance’, ‘Gustule’]
prenoms[1:][‘Amandale’, ‘Delphonse’, ‘Bertrance’, ‘Gustule’]
prenoms[:-1][‘Jobriel’, ‘Amandale’, ‘Delphonse’, ‘Bertrance’]
prenoms[:-2][‘Jobriel’, ‘Amandale’, ‘Delphonse’]
prenoms[-1]‘Gustule’ (équivaut à prenoms[4])
prenoms[::2][‘Jobriel’, ‘Delphonse’, ‘Gustule’] (un élément sur deux)
prenoms[::3][‘Jobriel’, ‘Bertrance’] (un élément sur trois)
prenoms[::-1][‘Gustule’, ‘Bertrance’, ‘Delphonse’, ‘Amandale’, ‘Jobriel’]
inverse les éléments

Il n’est pas toujours évident de manipuler les indices négatifs. Pour vous faciliter la tâche, il faut savoir que prenoms[-3] correspond à prenoms[len(prenoms) – 3] c’est-à-dire à prenoms[1].

[-1] est un indice négatif particulièrement intéressant car il permet d’accéder au dernier  élément d’une liste sans connaître la longueur de celle-ci.

Il est possible de parcourir une liste en utilisant un pas qui ne prend qu’un élément sur deux [::2] ou qu’un élément sur trois [::3] par exemple. Il existe une technique de slicing très intéressante: Un pas de [::-1] inverse les éléments d’une liste.

méthodes associées aux objets de type list()

Nous avons déjà vu list.append(), list.extend() et list.insert(). Il existe également :

  • list.sort()

Effectue le tri d’une liste dans l’ordre numérique croissant (ou dans l’ordre alphabétique croissant).

prenoms = ['Jobriel', 'Amandale', 'Delphonse', 'Bertrance', 'Gustule']
prenoms.sort()
print(prenoms)

Résultat: [‘Amandale’, ‘Bertrance’, ‘Delphonse’, ‘Gustule’, ‘Jobriel’]

list.sort() peut prendre l’argument reverse = True. Dans ce cas, le tri est décroissant :

prenoms = ['Jobriel', 'Amandale', 'Delphonse', 'Bertrance', 'Gustule']
prenoms.sort(reverse = True)
print(prenoms)

Résultat : [‘Jobriel’, ‘Gustule’, ‘Delphonse’, ‘Bertrance’, ‘Amandale’]

  • list.index()

list.index() retourne l’index de l’élément passé en argument. Si l’élément est absent de la liste, Python lève une exception. Si l’élément est présent plusieurs fois, la méthode retourne l’index de la première occurrence.

prenoms = ['Jobriel', 'Amandale', 'Delphonse', 'Bertrance', 'Gustule']
i = prenoms.index("Amandale")
print(i)

Résultat : 1

prenoms = ['Jobriel', 'Amandale', 'Delphonse', 'Bertrance', 'Gustule']
j = prenoms.index("Flatule")
print(j)

ValueError: ‘Flatule’ is not in list

Si une valeur est présente plusieurs fois, il est possible de passer en argument l’index à laquelle doit débuter la recherche sous la forme list.index(valeur, i):

prenoms = ['Jobriel', 'Amandale', 'Delphonse', 'Jobriel', 'Gustule']
i = prenoms.index("Jobriel", 2)
print(i)

Voir les articles

Résultat: 3  (la première valeur ‘Jobriel’ est ignorée)

  • list.reverse()

Cette méthode inverse les valeurs de la liste. Elle est équivalente à la technique de slicing [::-1]

prenoms = ['Jobriel', 'Amandale', 'Delphonse', 'Bertrance', 'Gustule']
prenoms.reverse()
print(prenoms)

Résultat: [‘Gustule’, ‘Bertrance’, ‘Delphonse’, ‘Amandale’, ‘Jobriel’]

  • list.pop()

list.pop() est une drôle de méthode. Elle supprime et retourne par défaut, la dernière valeur de la liste. Cette méthode peut prendre un indice en argument. Dans ce cas, c’est la valeur indicée qui est supprimée et retournée. Amusons-nous à poper deux exemples!

prenoms = ['Jobriel', 'Amandale', 'Delphonse', 'Bertrance', 'Gustule']
popped = prenoms.pop()
print(popped, prenoms)

Résultat: ‘Gustule’ [‘Jobriel’, ‘Amandale’, ‘Delphonse’, ‘Bertrance’]

prenoms = ['Jobriel', 'Amandale', 'Delphonse', 'Bertrance', 'Gustule']
popped = prenoms.pop(2)
print(popped, prenoms)

Résultat: ‘Delphonse’ [‘Jobriel’, ‘Amandale’, ‘Bertrance’, ‘Gustule’]

  • list.remove()

Cette méthode supprime la première occurrence de la valeur passée en argument. Voici un exemple avec une liste contenant deux « Jobriel ».

prenoms = ['Jobriel', 'Amandale', 'Delphonse', 'Jobriel', 'Gustule']
prenoms.remove("Jobriel")
print(prenoms)

Résultat: [‘Amandale’, ‘Delphonse’, ‘Jobriel’, ‘Gustule’]

  • list.count()

Cette méthode compte le nombre de fois où une occurrence apparaît dans une liste et elle retourne la valeur correspondante.

prenoms = ['Jobriel', 'Amandale', 'Delphonse', 'Jobriel', 'Gustule']
numb = prenoms.count("Jobriel")
print(numb)

Résultat: 2

Tester l’appartenance d’une valeur à une liste

L’instruction conditionnelle if… in autorise très facilement ce genre de test. Elle valide la présence ou l’absence de la valeur recherchée dans la liste.

prenoms = ['Jobriel', 'Amandale', 'Delphonse', 'Bertrance', 'Gustule']
if "Bertrance" in prenoms:
    print("Le test d'appartenance est positif.") 
else:
    print("Le test d'appartenance est négatif.")

Résultat : « Le test d’appartenance est positif. »

prenoms = ['Jobriel', 'Amandale', 'Delphonse', 'Bertrance', 'Gustule']
result = "Maxouelle" in prenoms
print(result)

Résultat : False

Chapitre 8 – Quelques méthodes sur les objets de type string( )

En Python, tout est objet. Les chaînes de caractères sont par exemple des objets créés par instanciation de la classe str(). Une classe, c’est un peu comme une caisse à outils qui permet de créer un objet et de le façonner. La classe contient des méthodes et des variables que l’on pourrait comparer aux outils d’un menuisier:

Lire la suite de « Chapitre 8 – Quelques méthodes sur les objets de type string( ) »

Chapitre 6 – Les fonctions

Une fonction est une portion de code qui effectue une tâche ou un calcul relativement indépendant du reste du programme. Une fonction comporte des paramètres d’entrée sur lesquels elle travaille avant de retourner une valeur de sortie qui peut être stockée dans une variable. Si la fonction ne retourne rien (None), on parlera plutôt de procédure. Une fonction est un sous-programme réutilisable partout dans le code. Il est donc inutile de réécrire des instructions identiques. Il suffit de les inclure dans une fonction et d’appeler cette dernière à chaque fois que cela est nécessaire.

Lire la suite de « Chapitre 6 – Les fonctions »

Chapitre 5 – Les booléens

Aujourd’hui, je vais vous parler des booléens. Les booléens sont des êtres farouches qui vivent au fin fond des forêts binaires recouvrant la terre sacrée du Grand Python Digital. Très difficiles à observer dans leur milieu naturel, bla, bla, bla… Bon en fait, il s’agit d’une notion de programmation un peu déroutante que j’avais du mal à conceptualiser au début de mon apprentissage du Python.

Lire la suite de « Chapitre 5 – Les booléens »