Initiation à Python

Auteur

Floss Manuals

Python, comme son nom l'indique, est un langage malléable qui repose sur une base puissante. Il est considéré comme un langage facile à apprendre parce qu'il y a une grande clarté dans l'expression en Python (comparé à d'autres évidemment). Python est à la croisée de plusieurs besoins, à mi-chemin entre différentes technologies et s'avère prêt à être employé dans de nombreux contextes avec une grande rapidité d'adaptation. Python est un compromis parfait pour ceux qui ne veulent pas multiplier les langages.

Commentez

- Titre : Initiation à Python

- Auteur : Floss Manuals

- Parution : 14 juillet 2014

- Public visé :

- Licence : Le contenu de cet article est rédigé par flossmanuals et est mis à disposition selon les termes de la Licence Creative Commons Attribution 3.0 non transposé. Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright ® 2013 Developpez.com.

Sommaire

- I. Introduction

- I-A. Quels sont les autres langages ?

- I-A-1. Les langages interprétés / compilés

- I-A-2. Les langages déclaratifs / structurés

- I-B. Pourquoi faire si compliqué ?

- II. À propos de ce livre

- III. Notions essentielles

- III-A. Premier programme

- III-B. Conserver le programme

- III-C. Découvrir les variables

- III-D. Les types simples : str, int, float

- III-E. Afficher des valeurs et résultats

- III-F. Interagir avec l'utilisateur

- III-G. Connaître et modifier un type

- III-G-1. type()

- III-H. Comparer des données

- III-I. Comparaisons de grandeur

- III-J. Associer des comparaisons

- IV. Structurer des programmes

- IV-A. Données structurées fondamentales

- IV-B. Plus sur le type chaîne (str)

- IV-B-1. Écrire avec des guillemets

- IV-B-2. Utiliser des guillemets simples

- IV-B-3. Échapper les caractères avec \

- IV-B-4. Les caractères spéciaux

- IV-B-5. Agir sur le texte

- IV-C. Listes simples et tuples

- IV-D. Les dictionnaires

- IV-E. Utilisation des types

- IV-F. Sélectionner des éléments dans les structures

- IV-F-1. Sélection par position (index)

- IV-G. Sélection multiple et longueur dans les structures

- IV-H. Parcourir une structure de données

- IV-H-1. While

- IV-H-2. For

- IV-H-3. range()

- IV-H-4. Parcours de dictionnaires

- IV-I. Ajout et concaténation d'éléments

- IV-J. Suppression d'éléments

- IV-K. Affectation et remplacement de valeur

- IV-L. Trier des listes

- V. Modulariser

- V-A. Regrouper dans des fonctions

- V-B. Rendre les fonctions plus universelles avec des paramètres

- V-C. Paramètres inconnus

- V-D. Réutiliser le résultat d'une fonction

- V-E. Faciliter la réutilisation avec les classes

- V-F. Définir des classes

- V-G. Ajouter des attributs

- V-H. Ajouter des méthodes

- V-H-1. Des méthodes standards

- V-H-2. _init__ et self, une méthode particulière

- V-I. Manipulations d'attributs

- V-I-1. Manipuler les attributs

- V-I-2. Attribut de classe

- V-J. Privatiser attributs et méthodes

- V-K. Paramètres d'initialisation d'instances

- V-L. Étendre ou dériver une classe

- V-M. Les modules : partager des fonctions et objets entre programmes

- V-M-1. Utilisation du module

- VI. Aller plus loin avec Python

- VI-A. Test de réussite d'une fonction (try…else)

- VI-B. Gestion des fichiers et chemins avec OS

- VI-C. Manipulation avancée de texte avec RE

- VI-D. Faciliter le débogage des programmes avec les tests

I - Introduction

Les langages informatiques sont nombreux, très nombreux. Un jour les ordinateurs parleront peut-être même plus de langues que les humains, pardon les humains pourront parler en plus de langues avec les ordinateurs qu'entre eux-mêmes.

Si vous vous lancez dans une première expérience de programmation, remarquez que l'on parle de langage et non pas de langues, mais en fait c'est bien le même effort qui est à l'œuvre : communiquer. Dans un cas, vous communiquez avec un congénère, dans l'autre avec un ordinateur.

L'avantage de communiquer avec un congénère, c'est la ressemblance qui vous unit à lui et qui fait que lorsque vous souriez, vos semblables comprennent que c'est normalement positif (ça dépend du type de sourire, mais la variation entre honnête et narquois ne trompe en général pas longtemps).

L'avantage de communiquer avec un ordinateur, c'est qu'il connaît déjà toute sa langue sur le bout des doigts. Rien à lui expliquer au niveau dictionnaire, grammaire. En revanche cet ami à l'inconvénient d'être entêté et de ne tolérer aucune erreur. Sans quoi, il ne comprend pas. l'intelligence de l'ordinateur s'arrête aux règles qu'on lui a inculquées et c'est bien pour cela que l'on programme, pas pour jacasser autour d'un verre, mais bien lui inculquer de nouvelles tâches qu'il réalisera à notre place ensuite. L'ordinateur est un robot et le langage de programmation le moyen de lui parler.

Au moins la relation est claire :

- vous pourrez être très directif avec l'ordinateur « fais-ci », « fais ça » et il en vous crachera pas à la figure ;

- vous pourrez lui faire faire ce que vous voulez « mets du rouge, du vert, du bleu et multiplie par 12 si la couleur est inférieure à celle de mon smoking ».

En revanche, il vous dira explicitement quand vos indications sont trop floues, peu précises, mal coordonnées… Vous récoltez la responsabilité de votre pouvoir.

Python, comme son nom l'indique, est un langage malléable qui repose sur une base puissante. Il est considéré comme un langage facile à apprendre parce qu'il y a une grande clarté dans l'expression en Python (comparé à d'autres évidemment). Python est à la croisée de plusieurs besoins, à mi-chemin entre différentes technologies et s'avère prêt à être employé dans de nombreux contextes avec une grande rapidité d'adaptation. Python est un compromis parfait pour ceux qui ne veulent pas multiplier les langages.

I-A - Quels sont les autres langages ?

On regroupe les langages selon plusieurs catégories qui sont mobiles, selon les besoins. Vous en connaissez déjà certainement, au moins de nom : HTML, CSS, Java (-script), C, C++, Qt, GTK, etc(1).

I-A-1 - Les langages interprétés / compilés

L'une des plus grandes différences entre les langages tient dans leur fonctionnement au moment de l'exécution. Les langages compilés sont autonomes et peuvent fonctionner d'eux-mêmes, mais seulement sur les environnements pour lesquels ils sont conçus. Les langages interprétés ont toujours besoin d'un interpréteur pour fonctionner, ils sont cependant en général plus portables. Les défenseurs du compilé mettent en général en avance les performances (rapidité d'exécution…) et ceux de l'interprété l'efficience (rapidité de création…).

I-A-2 - Les langages déclaratifs / structurés

Les langages déclaratifs sont parmi les plus utilisés par le grand public : on y comptera le HTML et le CSS ou des langages de ce type. Dans ce type de langage, toutes les actions sont effectuées quoi qu'il arrive, sans ouvrir de possibilité selon les contextes. Les langages structurés au contraire peuvent laisser plus de possibilité lors de l'exécution mais en sont d'autant plus compliqués à utiliser.

Python est un langage structuré, de nature interprétée, même si des procédés permettent de le compiler. Nous partirons du principe que vous souhaitez réaliser des projets de plus ou moins grande envergure et que Python est peut être le langage qui vous y aidera.

I-B - Pourquoi faire si compliqué ?

C'est souvent la remarque des débutants en programmation, c'est peut-être déjà la vôtre et, par expérience, elle ressort à chaque difficulté. Je préfère que l'on prenne les choses de l'autre côté. Les langages informatiques répondent à des besoins et ceux-ci peuvent s'exprimer de façon différente. Après tout, pourquoi ne parle-ton pas tous anglais ou chinois, ce serait plus simple.

La seconde chose, ce n'est pas la programmation qui est compliquée, c'est ce que l'on souhaite faire ou dire. Vous limitez-vous souvent à des phrases sujet-verbe-complément ? Certainement pas. En programmation, c'est identique : il y a des règles simples, mais en général, on en veut plus. En clarifiant bien les objectifs et en préparant bien la façon d'y arriver, cela rend en général les choses beaucoup plus faciles.

II - À propos de ce livre

Ce livre a été débuté en janvier 2013 par Cédric Gémy, graphiste et formateur au sein d'Activdesign. Membre actif de FlossManuals, il a déjà mis à disposition plusieurs livres sur Gimp dont l'Initiation à Gimp de FlossManuals, mais a aussi participé à Fontes Libres, Créer un Epub, Scribus et Cinelerra.

Membre de l'équipe de Scribus, il utilise régulièrement Python pour créer des extensions au logiciel.

Elisa de Castro Guerra, a participé à la relecture attentive de ce livre.

Vous pouvez aussi contribuer à cet ouvrage ou à tout autre documentation libre sur le site de FlossManuals.

III - Notions essentielles

III-A - Premier programme

Lancer l'interpréteur Python. En ligne de commande, cela est très facile. Ouvrez un terminal et écrivez python. Ensuite, à l'invite Python commençant par >>>, écrivez

1+1

Programmer c'est comme utiliser une calculatrice, pas plus compliqué. Ce qui est compliqué, c'est ce qu'on veut faire et savoir comment le faire. Le reste, c'est Python qui le fera. Mais pour cela, vous devrez bien définir que vous souhaitez obtenir.

soustraction : -

multiplication : *

division : /

division résultat entier : /

modulo : %

parenthèses : ()

III-B - Conserver le programme

Lancer un éditeur de texte ou encore un éditeur Python. Écrivez alors les lignes suivantes :

#!/usr/bin/python
1+1

Enregistrez le fichier sous le nom ex1.py.
Exécuter en ligne de commande dans un terminal avec : python ex1.py.

L'enregistrement des programmes Python dans un fichier n'est pas nécessaire mais c'est cependant le seul moyen fiable de préserver les lignes écrites pour pouvoir les réutiliser. En effet, si dans le cadre de ce cours il suffira bien souvent d'utiliser l'historique de l'interpréteur Python, dans le cadre de projet plus complets, il sera plus pratique de tout enregistrer. Cela facilitera en particulier le débogage.

À l'heure actuelle, pour ceux qui utilisent des systèmes récents et Python 3, il est conseillé d'écrire dans un logiciel compatible utf-8 ce qui permet de ne pas voir de messages d'erreur apparaître dès qu'un accent est trouvé dans le fichier. Dans ce cas, c'est une bonne idée d'en informer Python en rajoutant une ligne spécifique en début de fichier :

-*- coding: utf-8 -*-

ce qui donne au complet :

#!/usr/bin/python
1+1

Évidemment, ces quelques lignes ne sont pas très sexy et ne réalisent pas grand-chose d'extraordinaire, mais en programmation, tout projet commence avec des choses simples. Le vrai problème de ce programme et de ne laisser aucune variation dans les données : il ne peut servir qu'à faire ce calcul particulier soit 1+1, ce qui est un peu limitatif. Un programme devrait pouvoir s'adapter à différentes situations.

III-C - Découvrir les variables

La variable est un élément d'un programme dont la valeur pourra changer en fonction des besoins. Il s'agit d'une sorte de boîte de stockage (tiroir) dont le contenu pourra varier.

Ainsi au lieu d'écrire :

1+1=2

on peut écrire :

a+a=b

le calcul sera toujours valable même si on remplace 1 par 2. Seul le résultat sera adapté.

2+2=?

La variable sera donc bien souvent une simple lettre ou un mot qui identifie les éléments à traiter ou à calculer. Cela va rappeler quelques heures douloureuses des maths au collège, mais tout va bien se passer.

Il faudra assigner (affecter) la valeur à la variable a avant que le calcul soit possible en Python. Il s'agit simplement d'informer des chiffres qui doivent être pris en compte dans le calcul et intervenir en remplacement des variables (ici les lettres).

L'affectation se fait à l'aide de l'opérateur =

a=2
b=a+a
b

Affiche : 4

Le nom des variables doit être le plus parlant possible. Dans notre cas, a ou b ont l'avantage d'être courts, mais n'expriment pas grand-chose. On pourrait utiliser des noms de variables comme age, largeur, hauteur…

Le nom des variables est libre. Cependant, il ne peut contenir que des lettres, chiffres et underscore. Enfin il ne peut commencer par un chiffre et ne doit pas être identique à un mot réservé du langage Python. Cette contrainte est importante pour éviter toute confusion.

Dernier point important qui exigera un peu de rigueur : Python est sensible à la casse. Essayez :

a=2
b=A+100

NameError: name 'A' is not defined

est le message renvoyé par l'interpréteur pour vous spécifier qu'il n'a pas compris votre instruction. Ici il vous précise qu'il ne connaît aucune variable nommée 'A'.

Comme on peut le voir dans les exemples précédents, l'affection peut être simple ou composée. Elle peut soit attribuer une valeur fixe a=2, soit attribuer un calcul b=a+a. Dans ce dernier cas, b sera toujours le résultat de ce calcul quel que soit la valeur donnée à a. Cependant, cette valeur est calculée au moment où l'affectation est effectuée.

a=2
b=a+a
b

Affiche : 4

Mais si l'on continue :

a=3
b

affiche : 4

Il faudra alors spécifier à nouveau l'affectation et le calcul :

a=3
b=a+a
b

affiche : 6

Ainsi, une affectation est valable tant qu'elle n'a pas été remplacée par une nouvelle et le résultat d'une opération tant que celle-ci n'a pas été reconduite.

III-D - Les types simples : str, int, float

Les variables sont typées. Cela signifie qu'elles acceptent un certain type de contenu, pas d'autre, un peu comme une voiture qui ne fonctionne pas avec tous les types de carburant.

Les deux types principaux et les plus simples sont les types permettant l'interprétation des nombres entiers (pour faire des calculs si besoin) et du texte (pour évaluer par exemple ce qu'un utilisateur a saisi comme mot de passe ou autre).

Le typage, c'est-à-dire l'attribution d'un type, se fait principalement au moment de la déclaration :

a=2

signifie implicitement que la valeur de a est de type entier car 2 est bien un nombre entier.

nom="cedric"

Signifie implicitement que la valeur de cedric est de type texte (nous dirons « chaîne », comme une suite de lettres).

Il existe de nombreux types, mais entier et chaîne sont les plus simples et fréquents. Nous pouvons y ajouter dès maintenant le type float qui est prévu pour les nombres décimaux. Nous obtenons donc l'association suivante :

- a=2  integer   nombre entier ;

- a="texte"  string   chaîne de caractères ;

- a=2.0  float   nombre décimal.

Là où il va être important de donner une bonne attention aux types tient au fait qu'il est impossible de combiner des données de types différents dans un calcul :

a=2
nom="cedric"
b=nom+a

TypeError:cannot concatenate 'str' and 'int' objects

Python nous informe qu'on ne peut ajouter des chiffres et des lettres. Jusque-là tout est normal, même si dans certains cas, comme nous le verrons, cela devra être contourné. Vous devez vous dire « Mais qu'est-ce qu'il est bête ! ». C'est un peu vrai, mais vous conviendrez que les cas de devoir effectuer des calculs sur des mots sont assez rares ! Et puis regardez maintenant :

a=2
b=1.5
c=a+b
c

affiche : 3.5

Il est donc possible de faire des calculs entre des entiers et des flottants, bref des chiffres, tout est donc normal. Remarquez simplement la façon d'écrire les chiffres qui n'utilise pas une virgule mais un point (à l'anglaise).

III-E - Afficher des valeurs et résultats

Dans le cadre de l'écriture de nos programmes, nous avons souvent besoin d'afficher des résultats. Vous avez certainement remarqué que Python ne fait pas cela immédiatement :

a=2
b=3
c=a+b

il n'affiche pas le résultat de calcul pour c. Il faut donc lui dire. Jusqu'à présent, nous avons simplement provoqué cet affichage en écrivant simplement le nom de la variable :

c

affiche : 5

Mais comment faire si nous souhaitons afficher plusieurs informations. Par exemple :

a b c

affiche : SyntaxError: invalid SyntaxError

Il ne comprend pas ce que nous souhaitons faire et nous informe que la syntaxe de notre ligne est mal faite. Mettre à la suite les noms de variables n'est pas assez clair : doit-il les calculer ? Dans ce cas comment ? Les afficher ?

Il existe de nombreuses façons d'afficher du contenu, mais pour l'instant, l'utilisation de la fonction print sera amplement suffisant :

age=100
print age

affiche : 100

print "j'ai ", age," ans."

affiche : j'ai 100 ans

Remarquez ici que les mots qui doivent apparaître tels quels sont écrits entre guillemets et que les variables n'en ont pas besoin. Les virgules entre chaque partie de la phrase sont absolument nécessaires pour éviter toute erreur. Elles servent à Python pour comprendre que le texte se poursuit, finalement comme des parties d'une phrase. Il s'agit d'une concaténation : ajout d'informations à la suite les unes des autres.

III-F - Interagir avec l'utilisateur

Lorsque nous souhaitons réutiliser nos portions de codes, nous les enregistrons dans des fichiers de manière à ce qu'ils constituent de véritables petits programmes. Le problème que nous rencontrons dans ce cas est de pouvoir transmettre des informations au programme sans avoir modifié le code systématiquement. En particulier, si nous souhaitons calculer le périmètre d'une zone, le calcul étant systématiquement identique, il nous faudra cependant modifier les variables.
Pour :

perimetre=(largeur+hauteur)*2

notre programme serait :

largeur = 100
hauteur = 200
perimetre=(largeur+hauteur)*2
print perimetre

Pour éviter d'éditer notre fichier, ce qui est long et risque d'y introduire des erreurs par mégarde, nous pouvons mentionner à notre programme de demander à l'utilisateur de saisir les informations quand elles sont nécessaires :

largeur = input()
hauteur = input()
perimetre = (largeur + hauteur) * 2
print perimetre

Enregistrez votre programme sous le nom perimetre.py et exécutez-le dans une console/terminal :

python perimetre.py

Dans le cas où la valeur a récupérer est une chaîne de caractères, Python renverra une erreur. Mieux vaut utiliser raw_input.

nom_de_forme = raw_input()

De nombreuses personnes conseillent d'utiliser raw_input dans tous les cas de figure, mais il faudra alors transtyper la variable.

III-G - Connaître et modifier un type

Pour connaître le type d'une variable, il existe deux façons principales qui donnent des résultats sensiblement différents.

III-G-1 - type()

Pour connaître le type d'une variable, il suffit d'utiliser une fonction interne à Python qui s'écrit invariablement sous la forme :

type(nom_de_variable)

par exemple :

a=100
type(a)

qui renvoie : <type 'int'>

Cela est en particulier utile pour connaître le type qui résulte d'une opération :

a=100
b=0.5
c=a*b
type(c)

qui renvoie le résultat : <type 'float'>

Python a attribué tout seul le type.

Pour modifier le type de la variable, il faudra utiliser des fonctions spécifiques à chaque type de destination :

int() pour convertir en entier.

str() pour convertir en chaîne de caractères.

Pour informer Python de la variable à typer à nouveau, il suffit de donner son nom entre les parenthèses.

#reprendre programme avec raw_input >
largeur = int(raw_input())
hauteur = int(raw_input())
perimetre = (largeur + hauteur) * 2
print perimetre

III-H - Comparer des données

Nous avons déjà effectué des petits programmes. Mais ils ne suffiront certainement pas à la plupart des situations. C'est une étape importante que de programmer en prenant en compte de critères que nous, développeurs, n'aurions pas besoin nécessairement d'utiliser à un moment donné. La variable était déjà une façon de prendre en compte la diversité des valeurs possibles pour une information. Maintenant, il existera des cas dans lesquels le calcul ne peut être effectué ou ne doit pas l'être. Pour détecter ces cas, il suffira de comparer les données fournies avec des valeurs spécifiées pour s'assurer que certaines conditions sont remplies. Par exemple, gardons notre programme calculant le périmètre, mais incluons de quoi calculer les dimensions en fonction d'une unité fournie. Pour cela, nous demanderons à l'utilisateur de choisir entre deux possibilités, nous testerons ce qu'il a saisi et afficheront le résultat dans l'unité de son choix.

La comparaison la plus simple se fait à l'aide de if…else… s'écrivant sous une forme bien particulière :

if comparaison :
action à effectuer si la comparaison est valable
else :
action à effectuer si elle ne l'est pas

Dans cette structure de base, sont absolument nécessaires :
les : à la fin de la ligne if et else
la tabulation en début des lignes d'actions, nommées en général « bloc d'instruction ».

Les retraits permettent à Python de comprendre la suite et l'imbrication des actions et d'éviter au programme de se tromper de direction.

Revenons maintenant à notre exemple.

Pour demander les possibilités, nous utiliserons pour l'instant une simple solution : nous écrirons les unités possibles et attendrons que l'utilisateur saisisse la valeur. Nous utiliserons donc print et raw_input().

print "votre unité de mesure finale voulue : mètre ou centimètre ?"
unite = raw_input()

Il suffit alors de reprendre le programme tel que nous l'avions défini et de nous placer à la dernière ligne.

if unite == "mètre" :
print perimetre, " mètre" #nous considérons qu'il les a écrit en mètre
else :
print perimetre*100, " cm"

Si le nombre de conditions est supérieur à 2 la structure if…else… ne sera pas suffisante. Dans ce cas, nous pourrons avoir recours à sa variante : if…elif…elif…else…

Elle s'écrira plus précisément sous la forme :

if unite == "mètre" :
print perimetre, " mètre"
elif unite == "centimètre" :
print perimetre*100, " centimètre"
elif unite == "millimètre" :
print perimetre*1000, " millimètre"
elif unite == "kilomètre" :
print perimetre/1000, " kilomètre"

Une version plus courte d'écrire les tests simples est souvent utilisée. Il s'agit de l'opérateur dit ternaire.

Alors qu'une condition occupe quatre lignes avec la syntaxe de base, avec l'opérateur ternaire, tout sera sur une seule ligne. Cela peut être pratique quand les lignes se multiplient et les niveaux d'indentation aussi. La syntaxe est la suivante :

x = true_value if condition else false_value

La forme de cette expression peut sembler un peu étrange du fait qu'elle est inversée et fonctionne directement en mode affectation. Au lieu d'exprimer : « si une condition est vraie ou fait ceci, sinon on fait cela », on exprime : « on fait cela si une condition est vraie sinon va voir ailleurs ». Il s'agit d'une petite chose mais qui marche parfaitement pour les petits besoins. Évidemment, le rôle de l'opérateur ternaire n'est pas de prendre la place de blocs d'instructions complexes.

III-I - Comparaisons de grandeur

Dans la partie Comparer des données, nous avons testé si la valeur saisie par l'utilisateur correspondait exactement à celle que nous cherchions. Mais comment cela se passe-t-il si nous cherchons un ordre de grandeur, par exemple, savoir si un chiffre est plus grand qu'un autre ?

Pour cela, nous utiliserons des opérateurs de comparaison. Ces opérateurs, vous les connaissez déjà certainement et les utilisez presque tous les jours. Commençons par celui que nous avons déjà vu et passons aux plus courants :

- ==   égal à ;

- <   plus petit (inférieur) et strictement plus petit ;

- <=   plus petit (inférieur) ou égal ;

- >   plus grand (supérieur) et strictement plus grand ;

- =>   égal ou plus grand (supérieur).

Il en existe un autre moins évident et pourtant pratique :

- !=   différent de, qui sert justement de contraire à la simple comparaison d'égalité.

Les cas d'utilisation de ces opérateurs sont nombreux. Demander son âge à un utilisateur d'un site Web pour lui donner accès. Valider un code postal ou un numéro de téléphone. Donner accès à des messages ou un contenu dans une langue particulière… Bref, les applications ne manquent pas.

Complétons notre exemple pour qu'il prenne en compte ces critères : afficher les chiffres en kilomètres dès qu'ils sont supérieurs à 1000, les messages sont en anglais si la personne n'est pas française. Les modifications seront alors assez importantes. Le chiffre renvoyé ne sera pas le fruit de la saisie d'unité de l'utilisateur. Cette question ne sera donc plus nécessaire. En revanche, nous devrons demander la langue. Voilà ce que cela pourrait donner :

print "Quelle est votre langue ? "
langue = raw_input()
if langue != "fr" :
message = "hello, the length is "
else :
message = "Bonjour, la longueur est "
unite = "mètre"
if perimetre > 1000 :
if langue != "fr":br>
unite = "kilometer"
else :
unite = "kilomètre"
print message, perimetre/1000, unite
else
print message, perimetre, unite

III-J - Associer des comparaisons

Il est parfois nécessaire d'effectuer plusieurs comparaisons successives sur un même élément voire entre plusieurs éléments. La forme d'écriture la plus simple va demander d'écrire de nombreuses lignes sans pour autant augmenter la lisibilité :

n = input()

if n < 10 :

if n > 3 :
print "n est compris entre 3 et 10"

De la même façon, la ligne qui suit teste si le chiffre est bien dans la plage souhaitée :

n = input()

if n < 10 and n > 3 :
print "n est compris entre 3 et 10"

L'opérateur and permet ainsi d'associer les deux comparaisons en une seule. Dans ce cas, la condition est remplie si le chiffre est compris dans les deux expressions cumulées.

Nous pourrions souhaiter que seulement l'une des expressions soit validée. Dans ce cas, le recours à or sera plus judicieux :

if n < 10 or n > 20 :
print "ok"

sera valable si le chiffre saisi est inférieur à 10 mais aussi s'il est supérieur à 20.

Enfin, l'opérateur not peut être utilisé pour exclure une possibilité :

if n not 10 :
print "ok"

Dans ce cas, il agira un peu comme !=. L'avantage de ces opérateurs est cependant aussi de pouvoir s'appliquer à un type spécial, dit booléen et qui n'accepte que deux possibilités : True ou False

Ce type d'opérateur est utile pour mémoriser la réussite d'un test, sans pour autant que sa valeur soit préservée (par exemple, lorsque vous êtes connecté à un compte Web, on se souvient que vous êtes validé quand vous changez de page, mais il n'est pas nécessaire de renvoyer vos données), ou encore lorsque Python renvoie ce type comme résultat. Ainsi, on peut remplacer :

if n < 10 or n > 20 :
print "ok"

par

if n < 10 or n > 20 :
n_ok = True

et réutiliser ensuite ce résultat :

if n_ok == True :
print "ok"

>> ok

ou

if n_ok is True :
print "ok"

>> ok

IV - Structurer des programmes

IV-A - Données structurées fondamentales

Jusqu'ici, nous avons essentiellement travaillé avec des chiffres qui sont des types de données relativement simples. Nous sommes parfois passés par le texte, mais sans rentrer dans le détail de sa manipulation. Tout cela est un peu limitatif et il sera souvent nécessaire de pouvoir associer plusieurs données et de garder leur relation. Certains types de données permettent de décrire de réelles structures et d'associer ainsi les différents éléments qui les composent de façon permanente ou temporaire. Parmi celle-ci, notons les chaînes de caractères justement, mais aussi les listes et les tuples qui sont un cas particulier de liste, ou encore les dictionnaires qui en sont une complexification. Enfin, les classes seront une variante étendue de ce principe, mais nous ne les verrons pas encore.

Pour connaître le type d'une donnée, vous pouvez à tout moment utiliser type(). Ainsi si vous avez une variable i avec la valeur 12, vous pourrez savoir si elle est de type str ou int :

i = 12
type(i)

Affiche : <type 'int'>,

alors que :

i = "12"
type (i)

Affiche : <type 'str'>.

Les chaînes de caractères sont des suites de lettres. Les mots sont le type de chaîne de caractères le plus naturel. On peut donc considérer qu'avec Python, les mots sont de simples suites de lettres analysées et rendues une à une. Le mot n'est que cet ensemble de lettres mises les unes à la suite des autres, comme les maillons d'une chaîne.
Voici un exemple de chaîne :

phrase = "je suis une chaîne de caractères"

globalement, toute valeur affectée à une variable entre guillemets est comprise par Python comme une chaîne de caractères, même si ce sont des chiffres qui sont entre les guillemets.
Pour changer un type il faudra utiliser une déclaration associée, déjà mentionnée :
str() pour l'utiliser en chaîne et int() pour l'utiliser comme un entier.
Ainsi si :

i = 12
type(i)

> <type 'int'>

en faisant

str(i)
type(i)

> <type 'int'>

On voit bien que i n'a pas changé de type, alors que :

n = str(i)
type(n)

> <type 'str'>

affecte le type à une nouvelle variable et évite alors toute confusion.

IV-B - Plus sur le type chaîne (str)

Nous nous attarderons un peu sur le type string. En effet, la manipulation de chiffres avec les langages de programmation ne pose en général pas d'autres difficultés que les compétences mathématiques de l'auteur. Le texte, en comparaison, peut sembler d'un fonctionnement un peu étrange.

Le terme chaîne déjà peut surprendre et pourtant il exprime bien la réalité des choses : il s'agit d'une suite de lettres comme des maillons sont les éléments d'une chaîne métallique. Cela induit plusieurs choses qui rendent le texte « informatique » un peu différent de celui du quotidien :

- lorsque nous parlons, notre expression est fluide et continue, nous ne parlons pas lettre par lettre ;

- lorsque nous parlons, nous utilisons des structures grammaticales (sujet, verbes, compléments…) dont aucun logiciel n'a nativement connaissance. Le mot lui-même est quelque chose de difficilement repérable.

Tout cela induit des difficultés qui ne sont pas insurmontables, mais qui demandent un peu de démonstration parce que le texte est souvent un élément très important d'un projet informatique.

Allons donc petit à petit dans les détails :

texte = "Mon texte pour commencer"
print texte

> Mon texte pour commencer

IV-B-1 - Écrire avec des guillemets

Remarquez à nouveau qu'une chaîne se définit entre guillemets. Si vous omettez les guillemets voici ce qui se passe :

texte = Mon texte pour commencer

File "<stdin>", line 1
texte = Mon texte pour commencer
SyntaxError: invalid syntax

Bref, cela ne marche pas. En revanche, si votre texte doit lui-même contenir des guillemets, un autre problème va se poser :

texte = "L'ouvreuse m'a dit : "Donnez-moi votre ticket." Je le lui ai donné."

File "<stdin>", line 1
texte = "L'ouvreuse m'a dit : "Donnez-moi votre ticket." Je le lui ai donné."
^
SyntaxError: invalid syntax

Même erreur. Dans le premier cas, le guillemet manquait ici il est en trop. En fait, Python interprète le guillemet avant Donnez comme le guillemet de fermeture de la chaîne et ne comprend pas ce qui se passe par la suite. Il existe plusieurs solutions pour contourner cette difficulté :

IV-B-2 - Utiliser des guillemets simples

La solution la plus simple consiste à utiliser des guillemets simples (apostrophe) en remplacement de guillemets doubles :

texte = 'Mon texte "essai" pour commencer'

>>> print texte

Mon texte "essai" pour commencer

Cela marche bien ici, mais posera un problème dans cet autre exemple :

texte = 'L'ouvreuse m'a dit : "Donnez-moi votre ticket." Je le lui ai donné.'

File "<stdin>", line 1
texte = 'L'ouvreuse m'a dit : "Donnez-moi votre ticket." Je le lui ai donné.'
^
SyntaxError: invalid syntax

Comme des apostrophes sont présentes dans le texte, il y a ici un nouveau conflit. Dans des cas comme ceux-ci, un peu complexe, il faudra trouver une autre solution.

IV-B-3 - Échapper les caractères avec \

L'échappement de caractère consiste à différencier au sein du texte les caractères a priori identiques mais qui doivent être traité de façon différente par Python. Il s'agit de dire : ceci est du code Python et ceci fait partie du texte à afficher.

Le principe est simple : il suffit d'ajouter un \ devant les guillemets ou caractères qui appartiennent au texte à afficher :

texte = "L'ouvreuse m'a dit : \"Donnez-moi votre ticket.\" Je le lui ai donné."

>>> print texte

L'ouvreuse m'a dit : "Donnez-moi votre ticket." Je le lui ai donné.

Le problème est souvent de savoir ce qu'il faut échapper. Généralement, il s'agit de tout caractère syntaxique du langage Python qui peut entrer en conflit avec un caractère du texte. Par chance, Python a limité au maximum sa syntaxe pour remplacer l'utilisation de caractères par l'indentation, donc cela arrive ici moins souvent que dans d'autres langages.

Globalement, il s'agit de :

- " à remplacer par \" ;

- ' à remplacer par \' ;

- et de \ à remplacer par \\.

Le caractère d'échappement est aussi utilisé dans un contexte légèrement différent lorsqu'il s'agit d'utiliser des caractères invisibles.

IV-B-4 - Les caractères spéciaux

Il s'agit toujours de quelque chose de spécial, ces caractères spéciaux ! Le tout est de avoir ce qui est spécial ou pas. Pour un Anglais, un é est un caractère spécial, pas pour nous. il pourra donc sembler étrange à certains.

- La méthode traditionnelle pour écrire des caractères est d'utiliser leur valeur dans la table des caractères ASCII avec chr() :

print chr(102)

> f

le problème est d'avoir accès à de vrais caractères étranges :

print chr(255)

> ?

print chr(196)

> ?

Ici, ces caractères sont simplement introuvables d'où le résultat identique. Il faudra utiliser unichr() en remplacement :

print unichr(255)

> ÿ

print unichr(196)

> Ä

Reste donc à connaître la correspondance entre ces chiffres et le caractère recherché. Un petit tour sur http://fr.wikipedia.org/wiki/Table_des_caractères_Unicode_(0000-0FFF)  peut aider.

Parmi les caractères spéciaux, on retrouve les caractères invisibles en texte, en général ceux qui permettent de faire des retours à la ligne ou des tabulations. Dans ces cas très fréquents, Python intègre des solutions standards utilisées dans de nombreux langages par des lettres de raccourci échappées \n pour une nouvelle ligne et \t pour une tabulation.

texte = "L'ouvreuse m'a dit : \n\t\"Donnez-moi votre ticket.\" \nJe le lui ai donné."
print texte

>
L'ouvreuse m'a dit :
"Donnez-moi votre ticket."
Je le lui ai donné.

IV-B-5 - Agir sur le texte

Modifier la casse

Passer le texte en majuscules ou en minuscules peut avoir plusieurs usages : en particulier, il permet de faciliter des recherches (voir ci-dessous) ou encore d'augmenter la lisibilité en différenciant les lettres dans le terminal sur lequel il n'est pas possible de faire de différenciation de style (gras, italique…).

Python possède deux fonctions de changement de casse : mettre en majuscule avec upper(), mettre en minuscule avec lower().

cherche = texte.upper()
print cherche

> L'OUVREUSE M'A DIT : "DONNEZ-MOI VOTRE TICKET." JE LE LUI AI DONNé.

cherche = texte.lower()
print cherche

> l'ouvreuse m'a dit : "donnez-moi votre ticket." je le lui ai donné.

Chercher une lettre ou un mot

Dans les processus d'automatisation, il n'est pas rare d'avoir à comparer des données. Dans un texte, nous aurons parfois besoin de savoir si une portion de texte est incluse dans une autre. Cette opération s'effectue à l'aide de la méthode find().

texte = "L'ouvreuse m'a dit : \"Donnez-moi votre ticket.\" Je le lui ai donné."
texte.find("ticket")

> 39

texte.find("billet")

> -1

Le résultat, s'il est valide, renvoie la position de l'occurrence dans le texte. Ainsi si le résultat est inférieur à 0 c'est que le texte n'a pas été trouvé. Comme les lettres majuscules et minuscules représentent des caractères différents en ASCII ou unicode, il est compréhensible que Python n'y soit pas indifférent et les différencie lors de la recherche.

texte.find("don")

> 61

texte.find("Don")

> 22

Pour faire des recherches un ignorant la casse, il faudra soit modifier la casse du texte avant recherche soit utiliser des expressions régulières (qu 'il n'est pas encore temps de voir).

Remplacer une partie du texte

Il en est de même du remplacement. Il suffira d'utiliser la fonction replace() pour remplacer toutes les occurrences d'un texte par autre.

print texte.replace("ticket","billet")

L'ouvreuse m'a dit : "Donnez-moi votre billet." Je le lui ai donné.

texte = "L'ouvreuse m'a dit : \"Donnez-moi votre ticket.\" Je le lui ai donné ce ticket."
print texte.replace("ticket","billet")

> L'ouvreuse m'a dit : \"Donnez-moi votre billet.\" Je le lui ai donné ce billet.

Découper le texte

Enfin, il est parfois nécessaire d'avoir de portions du texte. Cela est particulièrement vrai dans différents formats textes comme le CSV ou autre. Imaginons simplement des données utilisateurs contenant ces informations :

M,Alphonse,1932

M pour le sexe, Alphonse pour le prénom et 1932 pour la date de naissance. En tant que chaîne, chacune des informations n'a pas de réalité en propre. En utilisant split(), Python aura connaissance que nous souhaitons les traiter différemment. Il suffira de placer dans les parenthèses le caractère présent dans le texte qui doit servir à faire le découpage :

texte = "M,Alphonse,1932"
print texte.split(",")

> ['M', 'Alphonse', '1932']

Le texte obtenu n'est plus une chaîne, mais une liste qui, elle, se manipule différemment, c'est ce qu'il est temps de voir.

IV-C - Listes simples et tuples

Les listes et les tuples sont de simples suites d'éléments indexés. Dans les listes ou dans les tuples, les éléments qui se suivent ne sont pas nécessairement de même type. Alors que dans une chaîne de caractères, tout élément est compris comme une lettre, dans une liste, un élément chiffre (int) peut suivre un élément chaîne de caractères (str)…
Il existe deux façons de déclarer une liste :

- soit en créant une liste d'emblée, vide ou non :

liste1 = []
liste2 = [1,2,3]
type(liste1)

> <type 'list'>

type(liste2)

> <type 'list'>

- soit en typant la variable :

liste3 = list()
type(liste3)

> <type 'list'>

Le tuple héritera des mêmes procédés :

tuple1 = ()
tuple2 = (1,2,3)
tuple3 = tuple()
type(tuple1)

> <type 'tuple'>

type(tuple2)

> <type 'tuple'>

type(tuple3)

> <type 'tuple'>

La seule différence visible entre un tuple et une liste consiste dans l'utilisation de crochets dans le cas de la liste et de la parenthèse dans le cas du tuple. Au niveau de l'usage, nous verrons bientôt que la liste est modifiable alors que le tuple est prévu comme un élément figé dont les valeurs ne peuvent être modifiées.

IV-D - Les dictionnaires

Enfin, parmi les structures de données fondamentales en Python, on notera les dictionnaires : ce sont des listes un peu plus complexes, en ce que chacun des éléments qui les compose est au moins composé de deux membres, l'un étant la clé, l'autre la valeur, le tout embrassé par des accolades { }. La clé est la valeur sont associées par un ":" et les membres se suivent, séparés par des virgules comme dans des listes. L'utilisation d'un dictionnaire est en particulier utile lorsque les clés sont des mots qui permettent ainsi d'avoir une approche sémantique des données :

dictionnaire1 = {}
dictionnaire2 = {'nom':'Descartes','prenom':'René'}
dictionnaire3 = dict()
type(dictionnaire1)

> <type 'dict'>

type(dictionnaire2)

> <type 'dict'>

type(dictionnaire3)

> <type 'dict'>

Ainsi, en imaginant deux variables différentes elles peuvent être représentées sous les formes :

- chaînes de caractères :

nom = "Descartes"
prenom = "René"

dans ce cas, rien n'associe informatiquement les deux variables, seul le développeur connaît la relation implicite que le nom entretien avec le prénom.

- liste ou tuples :

personne = ["Descartes","René"]

dans ce cas, les deux valeurs sont associées dans un ensemble qui peut être signifiant, mais la nature des éléments n'est pas mentionnée. Cela peut être trompeur dans des cas comme :

personne = ["David","Vincent"]

Comment savoir lequel est le nom et lequel le prénom :

personne1 = {'nom':'Descartes','prenom':'René'}

qui enlève toute confusion possible quels que soient les noms et prénoms.
Les dictionnaires permettent de nombreuses autres applications grâce à des spécificités que nous traiterons ultérieurement, comme les classes, lorsque nous aurons avancé sur notre connaissance et compréhension des mécanismes de Python.

IV-E - Utilisation des types

IV-F - Sélectionner des éléments dans les structures

Connaître l'existence de ces structures ne suffit pas, il faut aussi pouvoir les manipuler. Alors que dans le cas des entiers (int) la manipulation se concentrait sur des opérations mathématiques, ces structures nouvelles étant une façon d'associer des éléments par ailleurs séparés ou compris comme tels, la plupart des procédés seront relatifs à la manipulation des éléments. L'ajout de données est le plus simple.

La sélection des éléments est évidemment fondamentale, soit pour extraire la donnée et l'exploiter dans un nouveau contexte, soit pour la modifier.

Elle peut se faire de deux façons : soit par la position des éléments plutôt utilisée pour les chaînes et listes, soit par la valeur.

IV-F-1 - Sélection par position (index)

Les chaînes, la liste et le tuple

L'accès aux éléments se gère en général par un chiffre qui représente sa position dans l'ensemble. Ce chiffre, nommé indice, est en général exprimé entre crochet quel que soit le type de structure utilisée :

phrase = "Je pense donc je suis."
print phrase

> Je pense donc je suis.

print phrase[1]

Cette dernière ligne permet d'extraire la lettre placée en position 1 de la chaîne phrase dont la valeur est « Je pense donc je suis. » Le résultat attendu est donc ?

> e

Ici, Python renvoie « e ». Vous direz oui mais la première lettre est le « J ». Certes, mais en Python comme dans de nombreux cas en informatique, on pourra estimer que la numérotation commence à 0. Il nous faudra donc tout décaler en cherchant notre indice :

print phrase[0]

> J

En ce qui concerne la liste ce sera la même chose :

liste1 = ("René", "Descartes")
print liste1[1]

> Descartes

et le tuple

tuple1 = ["René","Descartes"]
print tuple1[1]

> Descartes

En cas de besoin, il est possible de retrouver l'index d'un élément par sa valeur :

liste1=("Descartes","Erasme","Montaigne","Montesquieu","Diderot","Rousseau","Voltaire")
liste1.index('Voltaire')

> 6

Le dictionnaire

En ce qui concerne le dictionnaire, on accédera à la valeur en nommant la clé :

dictionnaire1={'nom':'Descartes','prenom':'René'}
print dictionnaire1['nom']

> Descartes

Il est aussi possible d'utiliser :

dictionnaire1.get('prenom')

on voit que dans ce cas, l'avantage du recours au tableau est qu'il évite le recours à la position de l'élément. Ainsi, si les éléments ont changé de place suite à une manipulation, l'appel restera correct grâce à la clé, inchangée.

Sélection par valeurs

Cette méthode est plutôt utilisée dans des tests qui permettent de vérifier la présence d'un élément dans la séquence :

nom = "Descartes"
if "cartes" in nom:
print "Descartes est de la famille des cartomanciens"
else:
print "Descartes aurait dû s'appeler Descartes"

Nous aurons bien sûr le même comportement pour une liste véritable :

liste1=("Descartes","Erasme","Montaigne","Montesquieu","Diderot","Rousseau","Voltaire")
if "Voltaire" in liste1:
print "Voltaire est un philosophe"

> Voltaire est un philosophe

IV-G - Sélection multiple et longueur dans les structures

Dans le cas des chaînes surtout, mais quelquefois aussi des listes et tuples, il est parfois utile de sélectionner plusieurs éléments. Quand il s'agit de chaînes, cela est évident si on souhaite extraire plusieurs lettres faisant sens ensemble, comme un mot, par exemple. Dans le cas des listes et des tuples, cela se fera surtout sentir dans le cas de réaffectation, ou de suppression.
On pourra connaître la longueur de la structure en ayant recours à len().

phrase = "Je pense donc je suis"
len(phrase)

> 21

liste1("René","Descartes")
len(liste1)

> 2

Cela fonctionne aussi, de la même façon avec les tuples et les dictionnaires.
Ensuite et dans tous les cas, la longueur d'une sélection sera définie par les indices de début et fin de la sélection, séparé par un ":". Ainsi :

phrase = "Je pense donc je suis"
print phrase[3:8]

Affiche : > pense

N'oubliez pas que les indices commencent à 0 !
Lorsque l'on veut sélectionner à partir du début on écrira simplement :

print phrase[:8]

> Je pense

Lorsque l'on souhaite sélectionner les derniers éléments, les choses se compliquent un peu puisqu'il faut connaître la quantité d'éléments. Ainsi on pourrait faire :

liste4=("René","Descartes","Méditations Métaphysiques")
longueur = len(liste4)
print liste4[1:longueur]

> ('Descartes', 'Méditations métaphysiques')

Remarquez ici deux choses. Premièrement la spécificité du traitement des accents, et ensuite que le résultat est rendu sous forme de liste.
On pourra abréger en notant :

liste4 = ("René","Descartes","Méditations métaphysiques")
print liste4[1:len(liste4)]

IV-H - Parcourir une structure de données

L'objectif des structures de données est d'organiser le contenu pour y avoir ensuite accès de façon facilitée, et avec des outils qui permettent de les exploiter simplement selon des critères spécifiques. Il est en particulier régulièrement nécessaire de parcourir les éléments de la structure pour en extraire les valeurs ou faire une recherche.

Ici rien de bien nouveau, nous nous contenterons de montrer comment les boucles déjà vues s'adaptent à ces formes nouvelles.

IV-H-1 - While

phrase = "Je pense donc je suis"
i=0
while i < len(phrase):
print (phrase[i])
i+=1

On utilise ici la valeur incrémentée de i comme indice pour extraire la valeur. On retrouvera le même principe dans :

liste4 = ("René","Descartes","Méditations métaphysiques")
i=0
while i < len(liste4):
print (liste4[i])
i+=1

IV-H-2 - For

Avec for nous pourrons éviter le recours à l'incrémentation d'un chiffre en parcourant la liste en elle-même :

for element in liste4:
print(element)

> René
> Descartes
> Méditations Métaphysiques

Ainsi s'il s'agit de parcourir toutes les données, ce procédé pourra se révéler plus astucieux. While permettra d'avoir un meilleur contrôle des limites mais aussi de faire des boucles non pas élément après élément, mais tous les 2, 3, 4… éléments :

i=0
while i < len(phrase):
print(phrase[i])
i+=2

>
J

e
s

o
c
j

u
s

IV-H-3 - range()

Si vous préférez for quoi qu'il arrive, vous pouvez le combiner à range pour obtenir des résultats du même type, voire parfois plus succincts et plus clairs :

liste1=("Descartes","Erasme","Montaigne","Montesquieu","Diderot","Rousseau","Voltaire")
for n in (range(3,6)):
print liste1[n]

>
Montesquieu
Diderot
Rousseau

Range()  accepte plusieurs paramètres qui sont interprétés de la façon suivante :

- 1 seul paramètre, range(3) : indique une quantité de 3 ou encore range(len(liste1)) indique le nombre d'éléments de liste1 parcourue comme une plage ;

- 2 paramètres, range(3,6) : indique une plage, ici du quatrième au sixième ;

- 3 paramètres, range(0,7,2) : indique une plage ainsi qu'un rythme de sélection, ici tous les deux éléments. Nous avons dans ce cas :

for n in range(0,7,2):
print liste1[n]

>
Descartes
Montaigne
Diderot
Voltaire

IV-H-4 - Parcours de dictionnaires

La spécificité du parcours d'un dictionnaire tient dans le fait qu'il est composé de deux informations pour chaque élément : la clé et la valeur. Le parcours du dictionnaire peut se faire avec une boucle for mais si rien n'est spécifié, seul la clé est récupérée. Pour tout récupérer on utilisera une boucle du type :

dictionnaire1={'nom':'Descartes','prenom':'René','ecrit':'Meditations Métaphysiques'}
for cle, valeur in dictionnaire1.items():
print cle, " ", valeur

>
ecrit   Meditations Métaphysiques
nom   Descartes
prenom   René

Les informations sont bien écrites. Remarquez simplement qu'elles sont parcourues à partir de la fin du dictionnaire.
Pour ne récupérer que la clé, utilisez :

for cle in dictionnaire1.keys()

Pour ne récupérer que la valeur, utilisez :

for valeur in dictionnaire1.values()

IV-I - Ajout et concaténation d'éléments

Pour ajouter des éléments à un ensemble structuré, les procédés pourront varier. En ce qui concerne les tuples, rien ne sera possible puisqu'ils sont immuables.
Pour ajouter un élément dans une liste, la méthode employée sera dépendante de la position souhaitée. Pour positionner le nouvel élément en fin de liste, on utilisera la méthode append alors que pour insérer à un emplacement particulier, insert sera préféré.

liste1=['Rene','Descartes']
liste1.append('Philosophe')
print liste1

> ['Rene', 'Descartes', 'Philosophe']

liste1.insert(2,'France')
print liste1

['Rene', 'Descartes', 'France', 'Philosophe']

En ce qui concerne les dictionnaires, il suffira d'affecter une nouvelle clé et une nouvelle valeur en utilisant la clé comme indice :

dictionnaire1['pays']="France"

ou par la position :

dictionnaire1[2]="France"

Ensuite, il arrive régulièrement qu'il y ait plusieurs éléments à ajouter et que ces éléments proviennent éventuellement d'une autre variable de même type ou non. Si les types sont différents, une boucle pourra permettre l'opération, mais dans le cas ou les types sont identiques, une simple concaténation va suffire : la concaténation sur une simple opération de mise à la suite de deux éléments. La concaténation a déjà été entrevue avec la fonction print et les chaînes de caractères :

citation1 = "je pense"
citation2 = "je suis"
print citation1 + ", donc " + citation2

> je pense, donc je suis

Dans le cas des listes, il est possible d'utiliser la même opération :

liste1 = ['Rene', 'Descartes', 'France', 'Philosophe']
liste2 = ['Discours de la Méthode','Principes de la philosophie']
liste1 + liste2

> ['Rene', 'Descartes', 'France', 'Philosophe', 'Discours de la Methode', 'Principes de la philosophie']

Pour concaténer les deux sans passer par une opération, la méthode extend fera parfaitement l'affaire :

liste1 = ['Rene', 'Descartes', 'France', 'Philosophe']
liste2 = ['Discours de la Méthode','Principes de la philosophie']
liste1.extend(liste2)
print liste1

> ['Rene', 'Descartes', 'France', 'Philosophe', 'Discours de la Méthode', 'Principes de la philosophie']

IV-J - Suppression d'éléments

La suppression d'éléments n'est pas l'opération la plus courante, l'ajout et l'affectation de nouvelles valeurs seront bien plus utilisées. Il n'est cependant pas inutile de savoir comment faire pour limiter la recherche et, éventuellement, accélérer les manipulations, boucles…
En ce qui concerne les listes, la suppression se fera par la position de l'élément, son indice :

print liste1

> ['Rene', 'Descartes', 'France', 'Philosophe', 'Discours de la Méthode', 'Principes de la philosophie']

del liste1[2]
print liste1

> ['Rene', 'Descartes', 'Philosophe', 'Discours de la Méthode', 'Principes de la philosophie']

Il est possible de supprimer plusieurs éléments en une seule opération à l'aide d'une plage de sélection :

del liste1[2:4]

soit par sa valeur avec la méthode remove à laquelle on passera la valeur à supprimer :

liste1.remove('Descartes')
print liste1

> ['Rene', 'Philosophe', 'Discours de la Méthode', 'Principes de la philosophie']

Dans le cas des dictionnaires, en plus de pouvoir réutiliser del comme pour les listes, on retrouve la sempiternelle attention aux clés et aux valeurs :

- clear() videra complètement le dictionnaire ;

- pop(cle) supprime la clé mentionnée ;

- popitem() supprimera la paire clé:valeur.

dictionnaire1={"nom":"Descartes","Prenom":"René","Pays":"France"}
del dictionnaire1['nom']
print dictionnaire1

> {'Prenom': 'René', 'Pays': 'France'}

Ici l'ensemble clé:valeur est purement et simplement supprimé. Ce ne sera pas tout à fait le cas avec pop qui renverra la valeur associée à la clé au moment de la suppression.

dictionnaire1={"nom":"Descartes","Prenom":"René","Pays":"France"}
dictionnaire1.pop('nom')

> 'Descartes'

print dictionnaire1

{'Prenom': 'René', 'Pays': 'France'}

Ces renvois pourront être utiles dans certains cas, associés à des fonctions que nous verrons prochainement.
Enfin popitem renvoie la paire clé:valeur sous forme de tuple au moment de la sélection. La paire supprimée est la première du dictionnaire. Cette forme de suppression, surtout utile dans les boucles, évitera les doublons, mais ouvrira le risque des pertes.

IV-K - Affectation et remplacement de valeur

L'affectation est une opération souvent effectuée en création. Les modes d'affectation par défaut ont été vus en début de section sur les types structurés. La plupart du temps, il suffira de donner l'indice et de mentionner la nouvelle valeur à lui donner :

liste1=['René', 'Philosophe', 'Discours de la Méthode', 'Principes de la philosophie']
liste1[2]="Discours de la méthode"
print liste1

> ['René', 'Philosophe', 'Discours de la méthode', 'Principes de la philosophie']

Au sujet des dictionnaires, il suffit de sélectionner par la clé est de modifier la valeur :

dictionnaire1={"nom":"Descartes","Prenom":"René","Pays":"France"}
dictionnaire1['Pays']='Suède'
print dictionnaire1

> {'nom': 'Descartes', 'Prenom': 'René', 'Pays': 'Suède'}

Enfin, en ce qui concerne les chaînes, il faudra effectuer un remplacement d'une portion de la chaîne par une autre en la réaffectant ou en l'enregistrant dans une nouvelle chaîne :

chaine1 = "Bonjour"
chaine1.replace("jour", "soir")
print chaine1

> Bonsoir

IV-L - Trier des listes

Il est parfois important de changer l'ordre des éléments. Cela peut être pratique pour des classements ou mettre en relation deux séquences. sort() va faire ce travail très simplement :

liste1 = [5,10,3,7]
liste1.sort()
print liste1

> [3, 5, 7, 10]

Cela marche aussi avec les éléments textuels de la liste :

liste1 = ["Descartes","Erasme","Montaigne","Montesquieu","Diderot","Rousseau","Voltaire"]
liste1.sort()
print liste1

> ['Descartes', 'Diderot', 'Erasme', 'Montaigne', 'Montesquieu', 'Rousseau', 'Voltaire']

En revanche, une fois encore mais de façon plus étonnante, cela ne pourra s'appliquer aux tuples, ni aux chaînes d'ailleurs :

nom = "cedric"
nom.sort()

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'sort'

Si la liste contient des éléments de type int ou float et des éléments str, alors les éléments de type str sont placés à la fin alors que la suite est bien ordonnée en ce qui concerne les nombres quelque soit leur type.

liste1 = ["Descartes","Erasme","Montaigne","Montesquieu","Diderot","Rousseau","Voltaire"]
liste2 = [5,10,7,3]
liste1 = liste1+liste2
liste1.sort()
print liste1

> [3, 5, 7, 10, 'Descartes', 'Descartes', 'Diderot', 'Erasme', 'Montaigne', 'Montesquieu', 'Rousseau', 'Voltaire']

liste3 = [1.5, 3.5, 6.2, 10.5]
liste1 = liste1+liste3
liste1.sort()
print liste1

> [1.5, 3, 3.5, 5, 6.2, 7, 10, 10.5, 'Descartes', 'Descartes', 'Diderot', 'Erasme', 'Montaigne', 'Montesquieu', 'Rousseau', 'Voltaire']

Avec les dictionnaires, les choses se gâtent un peu. Que trier ? Les clés, les valeurs ? En utilisant la fonction sorted, on obtient un premier résultat.

dictionnaire2 = {'nom':'Descartes','prenom':'René','age':'?'}
dictionnaire2

> {'nom': 'Descartes', 'age': '?', 'prenom': 'Ren\xc3\xa9'}

sorted(dictionnaire2)

> ['age', 'nom', 'prenom']

J'ai souvent vu des personnes effectuant un tri pour rapprocher les éléments similaires et éventuellement trouver des doublons, comme on le ferait en cliquant sur les colonnes dans un navigateur de fichiers. Pour trouver des éléments similaires, il y a bien plus simple, rapide et fiable, count().

nom = "cedric"
nom.count('c')

> 2

ou sur des listes :

liste1=["Descartes","Erasme","Montaigne","Montesquieu","Diderot","Rousseau","Voltaire"]
liste1.count('Descartes')

> 1

liste1.append('Descartes')
liste1.count('Descartes')

> 2

On trie aussi souvent pour connaître la plus petite et la plus grande valeur. Il est alors possible d'utiliser les fonctions de bases min() et max() :

liste2 = [5,10,7,3]
min(liste2)

> 3

max(liste2)

> 10

et même d'en effectuer la somme avec sum() ce qui évite parfois d'avoir recours à des procédés plus longs et complexes :

liste2 = [5,10,7,3]
sum(liste2)

> 25

min() et max() fonctionnent aussi sur les chaînes et les éléments textuels :

nom = "cedric"
min(nom)

> 'c'

max(nom)

> 'r'

liste1=["Descartes","Erasme","Montaigne","Montesquieu","Diderot","Rousseau","Voltaire"]
max(liste1)

> 'Voltaire'

V - Modulariser

V-A - Regrouper dans des fonctions

À l'heure actuelle, nos programmes sont composés de petites suites d'opérations qui s'enchaînent les unes à la suite des autres dans le même ordre. Nous avons vu que pour répéter une opération nous pouvions utiliser des boucles. Mais cela ne change rien au fait que l'enchaînement est parfaitement continu. Or, il est souvent nécessaire de répondre à une action de l'utilisateur effectuée à un moment donné, parmi d'autres. Chaque action faisant appel à un bloc d'instruction, chaque action pouvant être déclenchée par l'utilisateur à certains moments ou à tous moments, il est important d'avoir une conception des instructions qui ne soient pas complètement linéaires. Pour cela, nous utilisons des fonctions : ce sont des blocs d'instructions auxquels nous avons attribué un nom de façon à pouvoir les appeler, les déclencher au moment opportun. Cela prend la forme suivante :

def nomdelafonction():
bloc d'instruction

Il s'agit là de la portion de Python qui définit ou déclare la fonction, un peu comme on déclare une variable. Elle commence par le mot clé def suivi du nom de la fonction (selon les mêmes règles de nommage que les variables d'environnement), suivie elle-même de parenthèses qui permettent de la différencier de simples variables (et nous verrons qu'elles pourront être autrement utiles) et d'un : qui mentionne le début d'un bloc. Le bloc qui suit doit être indenté et toutes les lignes qui la composent indentées de façon similaire, comme dans le cas des boucles.
Pour appeler la fonction, nous noterons simplement son nom sous la forme :

nomdelafonction()

un appel de fonction peut être réalisé à n'importe quel moment du programme, y compris à partir d'une boucle, voire d'une autre fonction.

V-B - Rendre les fonctions plus universelles avec des paramètres

L'avantage d'une fonction est de pouvoir être réutilisée dans différents contextes. Mais en imaginant une fonction qui affiche un texte du type :

def affiche():
print "Bonjour"

que faire si nous souhaitons aussi afficher « bonsoir » ? Faut-il créer une autre fonction ? Et ainsi les cumuler au point de s'y perdre. Il semble évident que non. Python, comme tous les langages modernes, permet l'utilisation de paramètres. Ces paramètres sont des valeurs associées à des variables qui seront transmises directement à la fonction.

Les paramètres sont en général déclarés dans la déclaration de définition et remplacent les valeurs constantes dans le bloc. On obtient ainsi :

def affiche(mot):
print mot

et pour appeler la fonction :

affiche("bonjour")

> bonjour

affiche("bonsoir")

> bonsoir

Si plusieurs paramètres sont nécessaires, il suffit de les séparer par des virgules dans la déclaration et dans l'appel :

def affiche(mot, nom):
print mot + ', ' +nom

affiche("Bonjour", "Richard")

ce qui est encore plus intéressant en jouant avec des valeurs inconnues saisies par l'utilisateur, récupérées dans des sources de données ou autre :

def affiche(mot,nom):
print mot+', '+nom

print "Quel mot afficher ? "
mot = raw_input()
print "pour qui ?"
nom = raw_input()
affiche(mot,nom)

enregistré dans un fichier et lancé :

> Quel mot afficher ?
Bonjour
> pour qui ?
Richard
> Bonjour, Richard

Attention cependant c'est l'ordre des paramètres qui compte, pas leur nombre :

affiche(nom,mot)

> Richard, Bonjour

V-C - Paramètres inconnus

Lorsque le nombre des paramètres est inconnu, par exemple lorsqu'ils sont issus d'une liste, nous sommes face à un problème : il n'est pas toujours possible de les déclarer ou de les appeler.

De fait, nous avons deux possibilités qui répondent à des besoins différents. Si nous connaissons le nombre de paramètres mais ne savons pas s'il sera toujours possible de leur affecter une valeur, ou si nous ignorons complètement la nature et le nombre des paramètres.

Dans le premier cas nous pourrons attribuer des valeurs par défaut :

def (nom, mot="Bonjour"):
print mot+', '+nom

affiche("Richard")

> Bonjour, Richard

affiche("Richard","Bonsoir")

> Bonsoir, Richard

voire de façon plus explicite pour éviter tout risque de confusion en particulier si le nombre de paramètres :

affiche("Richard", mot="Bonsoir")

Dans le second cas, nous placerons une étoile (liste) ou deux (dictionnaires) devant un paramètre référent :

liste1 = [5,10,3,7]
def moyenne(*num):
print "La moyenne est de "+sum(*num)/len(*num)

moyenne(liste2)

> La moyenne est de 6

V-D - Réutiliser le résultat d'une fonction

Le bloc d'instruction va effectuer une ou plusieurs actions. le résultat de ses actions ne peut être utilisé qu'en interne ou avoir besoin de resservir dans une autre partie du programme. Imaginons par exemple un jeu dans lequel nous devons mémoriser un score suite à des choix opportuns ou non. Chaque choix déclenche l'ajout ou la suppression de point, et le total doit être réutilisé dans les actions suivantes. Nous pourrions dans ce cas avoir une fonction par action et une fonction pour le calcul des points. Les actions envoient leur résultat à la fonction calculant les points pour effectuer le calcul adéquat. Le total peut lui-même être utilisé pour donner des bonus ou faire des changements de niveau.

Chaque total peut donc être enregistré dans une variable globale ou renvoyé à la fonction qui l'appelle afin qu'elle en tienne compte dans le déroulé des actions du jeu.

Un cas simple : écrire un résultat provenant d'un calcul utilisé dans une fonction.
L'exemple de base pourrait être :

def calcul(param1):
param2=10
print "le résultat est "+ str(param1+param2)

calcul(5)

> le résultat est 15

on pourrait remplacer cet exemple par

def calcul():
return param1+param2

print "le résultat est "+ str(calcul(5))

> le résultat est 15

L'avantage de cette méthode est d'offrir une meilleure réutilisation de la fonction. Dans le premier cas, la fonction calcule et affiche le résultat elle-même, ce qui la rend inexploitable dans le cas où le calcul seul doit être effectué.

Dans le second cas, la fonction ne fait que le calcul et laisse le soin au contexte de savoir qu'en faire.

V-E - Faciliter la réutilisation avec les classes

De nombreuses personnes utilisent Python sans utiliser de classes et pourtant l'un des premiers arguments que l'on avance en présentant Python est son orientation 100 % objet. Il pourra peut-être vous sembler étrange de se passer d'une telle fonction du langage si elle est tellement centrale, voire comment certains font pour s'en passer.

C'est une question que je me pose régulièrement : pourquoi se passer de la programmation objet alors qu'elle est si simple, si pratique et offre finalement et sans effort de nombreuses possibilités.

Moi qui écris ces lignes et qui ne suis que simple graphiste ou artiste et loin d'être un programmeur hors-pair comme d'autres peuvent s'en vanter, voilà pourtant que, si je devais faire des statistiques de mon code produit, l'utilisation de la programmation objet représenterait au moins 90 % des lignes écrites et surtout conservées. J'espère que vous n'en conclurez pas que, comme je ne suis pas un expert, je me trompe. Si vous avez des doutes, il vous suffira de faire une petite recherche sur Internet et vous vous rendrez rapidement compte que les discours sont en faveur de la programmation objet.

Finalement, le point à retenir est que si je peux le faire, vous le pouvez, qui que vous soyez.
La programmation orientée objet est une forme supplémentaire d'organisation du code produit. Jusqu'ici, nous avons utilisé des blocs d'instructions, puis des fonctions, en passant par des variables. Le plus évolué en termes de structuration est sans conteste, jusqu'à présent, l'utilisation de fonctions. Cependant, nous avons des petits soucis avec ces fonctions. Par exemple, si nous programmons un site Internet, nous avons besoin d'une fonction qui vérifie si un utilisateur est connecté et une autre qui liste les fichiers d'un répertoire. A priori, ces deux fonctions n'ont rien à voir ensemble et elles seront cependant accessibles de la même façon, au même niveau. Un peu comme si vous rangiez les torchons avec les serviettes (je sais vous le faites peut-être et moi aussi, mais ce n'est pas une excuse).
En programmation objet, nous allons ajouter au-dessus de la fonction habituelle def un élément qui va permette de classer les fonctions selon leur contexte d'utilisation. Parti de ce principe simple, beaucoup de choses vont alors devenir possibles.

V-F - Définir des classes

Définir une classe se fait simplement en utilisant le mot réservé class suivi du nom personnalisé de la classe en tant que début de bloc d'instruction :

class MaClasse :
#instructions

En ce qui concerne le nom de la classe, il est de coutume de respecter toujours la même règle, par simplicité. Nous utilisons ici un nom composé sans séparation entre les termes mais avec une majuscule au début de chacun d'entre eux.
Une fois la classe créée, il va être possible de l'instancier, c'est-à-dire de l'utiliser dans un cas concret que l'on pourra appeler objet, tout comme chaque table est une instance de ce que peut être une table.
L'instanciation se fait simplement en faisant une affectation :

objet = MaClasse()

Malheureusement, si vous essayez ces quelques lignes dans l'interpréteur Python, vous n'obtiendrez pas grand-chose :

class MaClasse :
#instructions

File "<stdin>", line 3
^
IndentationError: expected an indented block

Le message n'est pas très explicite, mais il mentionne bien qu'il manque quelque chose à cette classe : nous devrons donc lui ajouter des méthodes et des attributs.

V-G - Ajouter des attributs

À quoi sert de créer des classes si elles ne contiennent rien ? A priori à rien. La première tâche fréquente dans la définition des classes est souvent la définition de variables, comme pour toute autre structure. On appellera alors la variable « attribut » car elle devient une propriété liée en propre à l'objet et n'existe que dans son contexte.

class MaClasse :
essai = "ok"

Notez au passage l'indentation, car la classe agit comme un bloc d'instruction structuré. Il faudra donc être vigilant à ce niveau.
Essayons maintenant de l'instancier.

objet = MaClasse()
print objet.essai

Affiche : ok

Remarquez lors de l'instanciation, des parenthèses sont ajoutées au nom de la classe et aussi que pour faire référence à une propriété, il faut que :

- celle-ci soit définie dans la classe ;

- l'objet qui la concerne soit instancié ;

- la rattacher à l'instance en plaçant un point entre les deux.

pour vérifier le type il est toujours possible d'utiliser la méthode type :

type(MaClasse)

<type 'classobj'>

Ainsi, l'attribut essai se retrouve associée à l'instance et il peut y avoir autant d'attributs du même type qu'il y a d'instance :

class MaClasse :
essai = "ok"

objet = MaClasse()
print objet.essai

> ok

objet2 = MaClasse()
print objet2.essai

> ok

objet2.essai = "wow"
print objet2.essai,  objet.essai

> wow ok

On voit bien dans cet exemple que la valeur de l'attribut a pu être modifiée dans le bloc principal avec une simple affectation et que pourtant, l'affectation ayant été effectuée dans le contexte d'une instance, seule la valeur associée à l'attribut de cette instance a été réellement affectée, l'autre instance n'ayant pas été modifiée le moins du monde.

V-H - Ajouter des méthodes

Il en est des fonctions comme des variables. Dans le cadre d'une classe, elles deviennent des « méthodes ». Cette différence de terme peut sembler capricieuse, elle permet cependant de toujours bien savoir à quel niveau on parle.

V-H-1 - Des méthodes standards

Les méthodes vont permettre de définir des regroupements de fonctions propres à notre contexte de manière à mieux les exploiter, en respectant systématiquement ce contexte.

La définition d'une fonction interne à une classe est très simple, puisqu'elle ne contient aucune différence avec la façon standard de la définir : on utilise le mot clé def suivi du nom de la fonction, un ":", puis à la ligne le bloc d'instruction indenté.

class MaClasse:
def affiche(self):
essai="ok"
return essai

objet = MaClasse()
print objet.affiche()

> ok

V-H-2 - _init__ et self, une méthode particulière

__init__ est une méthode particulière qui est lancée automatiquement lors de la déclaration d'une instance. On l'appelle « constructeur » parce qu'elle aide à poser les bases de toute l'instance. On y déclare en général les principaux attributs et on peut éventuellement y commencer du traitement par appel de certaines méthodes.

Dans l'exemple qui précède, l'utilisation du constructeur pourrait conduire à un programme comme celui-ci :

class MaClasse:

def __init__(self):
self.essai="ok"

def affiche(self):
return self.essai

objet = MaClasse()
print objet.affiche()

> ok

Le constructeur contient en général au minimal l'affectation de base des premières variables les plus importantes du programme de manière à s'assurer qu'elles sont bien déclarées et seront parfaitement partagées par la suite au sein des différentes méthodes.

Le mot-clé self permet quant à lui, de spécifier que l'action s'effectue bien au niveau de l'instance elle-même. Nous aborderons plus abondamment ce sujet en parlant ultérieurement de leur opposé, les attributs de classe.

Enfin, si __init__ est un constructeur, __del__ est un destructeur. Plus rarement utilisé en Python puisque l'interpréteur prend en charge la destruction des objets lorsqu'ils deviennent inutilisés, le destructeur permet cependant de mieux contrôler les événements et de déclencher éventuellement des actions à la destruction d'une instance, comme renvoyer un message, par exemple.

class MaClasse:
def __init__(self):
self.essai="ok"
def affiche(self):
return self.essai
def __del__(self):
print "objet détruit"

objet = MaClasse()
print objet.affiche()

> ok

objet.__del__()

> objet détruit

Ici, nous forçons la destruction de l'objet pour l'exemple, mais cette action est bien sûr réalisable dans tout projet, même si elle n'est pas nécessaire.

V-I - Manipulations d'attributs

V-I-1 - Manipuler les attributs

Jusqu'ici, nous avons observé que les attributs auront des valeurs redéfinissables pour chaque objet. Mais qu'en est-il si l'on souhaite avoir une valeur partagée entre plusieurs instances ou à l'inverse gagner un peu en définissant spécifiquement les valeurs pour une instance.

V-I-2 - Attribut de classe

Nous avons manqué un peu de précision en définissant précédemment les attributs. Nous avons présenté notre attribut essai de la même façon alors qu'il est apparu dans deux contextes différents : celui de la racine de la classe et celui de la méthode. Nous avons mentionné que cet attribut était lié à l'instance, ce qui n'est pas tout à fait juste. Prenons le point suivi pour aborder le sujet : si à l'inverse, nous souhaitions que la même valeur soit partagée par plusieurs instances, la syntaxe de l'attribut devrait être un peu différente : on le définira en début de classe en dehors de toute méthode ou on le préfixera avec le nom de la classe de manière que l'attribut y soit explicitement associé, et non pas aux instances :

class MaClasse :
essai = "ok"
def affiche(self):
print essai

objet = MaClasse()
objet2 = MaClasse()
objet.affiche()

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in affiche
NameError: global name 'essai' is not defined

On voit dans cet exemple qu'un petit problème se pose : il n'est pas possible d'accéder à l'attribut essai à partir de la méthode affiche. La première solution qui pourrait nous venir à l'esprit serait d'utiliser le mot clé self pour spécifier l'appartenance de l'attribut, mais voici :

class MaClasse :
essai = "ok"
def affiche(self):
print self.essai

objet = MaClasse()
objet.affiche()

> ok

objet2 = MaClasse()
objet2.affiche()

> ok

objet2.essai = "wow"
objet2.affiche()

> wow

objet.affiche()

> ok

Nous nous retrouvons alors dans la même configuration que précédemment et ne résolvons pas notre problème : l'attribut est associé à l'instance et n'est pas partagé avec celle-ci.
La solution consiste à associer explicitement l'attribut à la classe en le pointant comme tel. Au lieu d'utiliser le mot-clé self, nous utiliserons alors le nom de la classe pour bien préciser que c'est à son niveau que les opérations vont s'effectuer.

class MaClasse:
def __init__(self):
MaClasse.essai = "ok"

objet = MaClasse()
objet2 = MaClasse()
print objet.essai

> ok

print objet2.essai

> ok

print MaClasse.essai

> ok

MaClasse.essai = "wow"
print objet.essai

> wow

print objet2.essai

> wow

Ici, les deux instances partagent donc bien le même attribut et changer la valeur de cet attribut en la préfixant du nom de la classe, même au moment de la réaffectation, permet bien de spécifier que ce n'est pas au niveau de l'instance que les choses se jouent et que par conséquent, toutes les instances hériteront de cette nouvelle valeur.
Si les choses ne sont pas clairement définies, il est possible de se retrouver avec de petites surprises. Continuons dans la foulée des lignes précédentes :

class MaClasse :
essai = "ok"
def affiche(self):
print self.essai

objet = MaClasse()
objet.affiche()

> ok

objet2 = MaClasse()
objet2.affiche()

> ok

objet2.essai = "wow"
objet2.affiche()

> wow

objet.affiche()

> ok

print objet2.essai

> wow

MaClasse.essai = "test"
objet.affiche()

> test

objet2.affiche()

> wow

À interpréter cet exemple, on voit que l'attribut de classe qui a été redéfini dans le cadre d'une instance, ne prend plus en compte l'affectation de valeur faite à cet attribut de classe.
Ainsi il y a deux essais différents : celui de la classe créée en début de classe ou explicitement et l'attribut de la méthode. La méthode affiche fait référence à son propre essai qui hérite de celui de la classe au moment de la construction de l'instance, mais qui peut être redéfini séparément. Pour faire explicitement référence à l'attribut de classe, nous aurions dû écrire print MaClasse.essai et non pas self.essai.

V-J - Privatiser attributs et méthodes

Le but de la programmation objet est de mieux structurer le code pour mieux contrôler les contextes d'exécution. En effet, tout programme commence avec quelques petites lignes et finit souvent par plusieurs milliers et il devient alors plus difficile de retrouver ces marques et de bien continuer.
Les défauts de conception n'apparaissent en général pas au début des projets mais bien à la suite, lors des modifications successives, éventuellement réalisées par plusieurs personnes qui ont chacune leur vision des choses.
Différencier les attributs de classe et d'instance est une première approche améliorée, mais il est aussi possible de définir explicitement ce qui peut être de l'extérieur de l'objet. Jusqu'à présent, tout attribut et toute méthode peuvent être appelés du programme principal. Alors que dans la pratique certains d'entre eux seront certainement créés pour un usage interne en prétraitement d'informations. Ces attributs n'ont pas besoin, et ne devraient donc pas laisser cette tentation d'être accessible de l'extérieur.
Un petit exemple pour voir de quoi il s'agit :

class MaClasse:
def __init__(self):
self.__essai="Bonjour "
self.essai="le monde"
def affiche(self):
print self.__essai
def montre(self):
print self.__essai

objet=MaClasse()
print objet.essai

Le résultat sera bien : le monde.

Maintenant essayons avec :

print objet.__essai

File "private_public.py", line 15, in <module>
print objet.__essai
AttributeError: MaClasse instance has no attribute '__essai'

L'interpréteur nous informe que la classe n'a pas d'attribut de ce nom. Ce n'est évidemment pas exactement vrai, mais il garde le secret sur la possibilité de manière à ne pas nous tenter : __essai est privé et ne peut être utilisé que depuis une méthode de la classe :

objet.affiche()

> Bonjour

Comment avons-nous fait pour rendre cette variable privée ? Nous avons simplement rajouté deux _ en début de nom. Remarquez au passage que essai et __essai sont considérés comme étant deux attributs différents par l'interpréteur alors que pour le lecteur ils seront évidemment très similaires.
La même opération peut être réalisée avec les méthodes :

class MaClasse:
def __init__(self):
self.__essai="Bonjour "
def affiche(self):
print self.essai
def __montre(self):
print self.__essai, self.essai

objet=MaClasse()
objet.__montre()

File "private_public.py", line 15, in <module>

objet.__montre()

AttributeError: MaClasse instance has no attribute '__montre'

alors que :

class MaClasse:
def __init__(self):
self.__essai="Bonjour "
self.essai="le monde"
def affiche(self):
self.__montre()
def __montre(self):
print self.__essai, self.essai

objet=MaClasse()
objet.affiche()

renverra :

Bonjour le monde

V-K - Paramètres d'initialisation d'instances

Il ne sera pas rare, puisque c'est partiellement l'objet de la structuration en objet, d'avoir besoin de créer plusieurs instances de la même classe.
Cela est réalisable très simplement, en passant des paramètres à notre constructeur :

class MaClasse:
def __init__(self, nom):
self.__essai="Bonjour"
self.essai="le monde,"
self.nom=nom
def affiche(self):
print self.__essai, self.essai, self.nom

objet=MaClasse("Cedric")
objet.affiche()

Nous avons déclaré le paramètre nom puis l'avons associé à self.nom pour le réutiliser plus librement.
Le résultat sera donc :

Bonjour le monde, Cedric

On passera en paramètre de constructeurs les éléments réellement indispensables à l'utilisation et à l'identification de cet objet dans le programme. Pour les paramètres moins fondamentaux, ils pourront être passés à l'appel d'une méthode. Il faudra bien sûr que celle-ci l'accepte au préalable :

class MaClasse:
def __init__(self, nom):
self.__essai="Bonjour"
self.essai="le monde,"
self.nom=nom
def affiche(self, sexe):
print self.__essai, self.essai, sexe, self.nom

objet=MaClasse("Cedric")
objet.affiche("Monsieur")

> Bonjour le monde, Monsieur Cedric

V-L - Étendre ou dériver une classe

Un autre cas de réutilisation qui ne sera pas rare, sera souvent d'avoir besoin de créer une variante de l'objet, avec plus de méthodes, des attributs différents ou avec d'autres différences. Dans ce cas, vous avez deux solutions :

- soit utiliser la classe de base telle quelle et ne rien changer (cas lorsqu'on utilise des modules) ;

- soit modifier la classe d'origine pour définir les nouveaux besoins et les y intégrer ;

- soit créer une nouvelle classe qui sera basée sur la première mais qui prendra en compte spécifiquement ces différences.

Vous comprendrez à nous lire que cette dernière méthode est la plus fiable : elle permet de préserver une classe originale saine et exempte des cas particuliers qui sont alors simplement définis dans les classes filles.
Ce concept d'héritage est mis en œuvre simplement en passant le nom de la classe d'origine en paramètre de la classe fille :

class MaClasse:    def affiche(self):
print self.nom

objet=MaClasseFille("Cedric")
objet.affiche()

def __init__(self, nom):
self.essai="Bonjour"
self.nom=nom
def affiche(self):
print self.essai, self.nom

class MaClasseFille(MaClasse):
def affiche(self):
print self.nom

objet=MaClasseFille("Cedric")
objet.affiche()

ce qui affiche

Bonjour Cedric

La classe file peut donc posséder ses propres méthodes, voire redéfinir les méthodes de sa classe mère :

class MaClasse:
def __init__(self, nom):
self.essai="Bonjour"
self.nom=nom
def affiche(self):
print self.essai, self.nom

class MaClasseFille(MaClasse):
def affiche(self):
print self.nom

objet=MaClasseFille("Cedric")
objet.affiche()

Avec cet exemple, la classe fille n'affichera que le nom passé en paramètre lors de l'instanciation alors que dans la classe mère, le nom était concaténé à « Bonjour ». Il sera ainsi possible de dériver énormément de chose sans toucher à l'original et ainsi diminuer le risque d'introduction de bogues dans d'autres applications qui utiliseraient aussi cette classe. Le code devient alors réellement plus simple à maintenir et chaque programme devient plus sûr. Les efforts peuvent être mutualisés sur plusieurs classes, voire plusieurs applications, car toute modification effectuée sur la classe mère sera automatiquement reportée sur les classes filles qui en hérite, ce qui est fondamental lors d'un débogage.

V-M - Les modules : partager des fonctions et objets entre programmes

Un module est une classe ou ensemble de classes qui sera spécifiquement utilisé comme ressources dans d'autres programmes. Il a en général été conçu comme tel et s'avère très pratique lors de tâches répétitives.
La règle veut que les modules portent le même nom que la classe qu'ils contiennent. Ainsi MaClasse.py serait le nom de module courant pour le fichier contenant la classe MaClasse. Bien sûr, le module pouvant en contenir plusieurs, cette règle est adaptable.
Au niveau du fichier contenant les classes il est fréquent d'ajouter une condition de test d'exécution (en général en fin de fichier) :

if __name__ == "__main__"
pass
# autres instructions au lieu de pass

Le but de cette condition est de définir ce qui doit être exécuté si le module est exécuté comme corps principal du programme, sinon, cette partie est simplement ignorée.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

class MaClasse:
def __init__(self, nom):
self.essai="Bonjour"
self.nom=nom
def affiche(self):
print self.essai, self.nom

class MaClasseFille(MaClasse):
def affiche(self):
print self.nom

if __name__ == "__main__":
pass

Enregistrez ce fichier avec l'extension .py.

V-M-1 - Utilisation du module

Une fois votre module créé, vous pourrez alors l'importer pour le réutiliser dans tous vos projets  :

import MaClasse
objet = MaClasse.MaClasseFille("cedric")
objet.affiche()

Commentons un peu ce qui se passe ici :

- on commence par importer le module on utilisant son nom, sans l'extension. Par défaut, Python cherche dans le PYTHONPATH et dans le répertoire courant ;

- on instancie les objets en utilisant le nom du module puis le nom de la classe (ce qui n'était pas nécessaire jusqu'ici) ;

- enfin, l'instance  peut alors jouer son rôle normalement.

La méthode import va charger l'ensemble du fichier en vue de son exploitation ultérieure.

from param_classe import MaClasseFille
objet = MaClasseFille("Cedric")
objet.affiche()

> Cedric

Si le gain ici n'est pas très important à cause de la simplicité de notre fichier module, remarquez qu'avec cette façon de faire, il n'est pas nécessaire de nommer le module lors de l'instanciation. Si cela a l'avantage de faire saisir un peu moins de texte, si vous devez charger plusieurs modules, il faudra être certain qu'il n'y aura pas de conflit dans le nom des classes et méthodes utilisées, problème qui est résolu avec l'utilisation d'import seul et la préservation de son espace de nom.

VI - Aller plus loin avec Python

VI-A - Test de réussite d'une fonction (try…else)

VI-B - Gestion des fichiers et chemins avec OS

VI-C - Manipulation avancée de texte avec RE

VI-D - Faciliter le débogage des programmes avec les tests

(1)(désolé, etc n'est pas un langage, pas encore, un jour peut-être)
Retour à l'article