Académique Documents
Professionnel Documents
Culture Documents
JACK FELLERS
TABLE DES MATIÈRES
Sans titre
1. Programmation
2. Variables et expressions
3. Exécution conditionnelle
4. Fonctions
5. Itérations
6. Listes
7. Dictionnaires
8. Ensembles
9. Programmes Python
10. Extension des programmes
11. Script Python
Conclusion
SANS TITRE
Python
1
PROGRAMMATION
Mots clés
Contrairement aux langues humaines, le vocabulaire de Python est en fait
assez restreint. Ce « vocabulaire » constitue les « mots réservés », cela
signifie que les mots qui ont une signification très particulière pour Python.
Quand vous écrivez vos programmes, vous pouvez créer vos propres mots,
cela veut dire que des mots qui ont une signification essentielle pour vous et
qui sont nommés variables.
Vous pouvez choisir des noms pour vos variables, mais vous ne pouvez
pas utiliser l’un des mots réservés de Python comme nom de variable. Les
mots réservés en Python sont comme illustrés ci-dessous :
and del global not with as elif if or yield assert else import pass break
except in raise class finally is return continue for lambda try def from
nonlocal while
Nous apprendrons peu à peu ces mots réservés et la manière dont ils
sont utilisés au cours de ce e-book. Nous pouvons forcer Python à afficher
un message à l’aide de l’instruction suivante:
print ('Hello World!')
De cette manière, nous avons écrit notre première phrase Python
syntaxiquement correcte.
Notre phrase débute par la fonction print suivie d’une chaîne de texte de
notre choix entre guillemets. Les chaînes de caractères dans les instructions
d’impression sont entourées de guillemets simples ou doubles. La majorité
des programmeurs utilisent des guillemets simples, mais dans quelques cas,
cela peut poser un problème parce qu’ils peuvent être mal interprétés par
Python.
Maintenant que nous avons un mot et une phrase facile que nous
connaissons en Python, nous devons savoir comment entamer une
conversation avec Python afin d’interagir avec lui.
Avant de pouvoir converser avec Python, vous devez installer le
software sur votre computer et découvrir la façon de lancer Python sur votre
computer. Le processus d’installation est vraiment simple et guidé par une
interface graphique soignée. Vous trouverez de plus amples informations
sur le site officiel suivant https://www.python.org/downloads/. Voici une
excellente nouvelle : si vous êtes un utilisateur de Linux ou de Mac OS,
Python est vraiment probablement déjà installé sur votre système. Dans tous
les cas, ouvrez une fenêtre de terminal et tapez la commande python3 et, si
elle est installée, l’interpréteur Python commencera à fonctionner en mode
interactif et quelque chose de similaire apparaîtra. Observez l’exemple ci-
dessous :
pi@raspberrypi:~ $ python3
Python 3.7.3 (default, Dec 20 2019, 18:57:59)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
Prompt >>> est la manière dont l’interpréteur Python vous demande la
question suivante : « Que voulez-vous faire maintenant ? ». Python est prêt
à converser avec vous. Tout ce que vous devez faire, c’est parler le langage
Python.
Vous pouvez imprimer quelques phrases sur l'écran, comme dans
l’exemple suivant :
print('Bonjour')
print('Je viens en paix')
print('Je voudrais apprendre Python')
print 'parce qu'il est très puissant'
File "<stdin>", line 1
print 'parce qu'il est très puissant
^
SyntaxError: Missing parentheses in call to 'print'
>>>
La conversation s’est bien déroulée pendant un moment, puis vous avez fait
une petite erreur en utilisant le langage Python. À ce stade, vous réalisez
que, même si Python est incroyablement difficile et puissant, il est vraiment
pointilleux sur la syntaxe que vous utilisez. En fait, Python n’est pas aussi
intelligent que vous le pensez, vous ne faites que converser avec vous-
même, mais vous utilisez la syntaxe requise. En un sens, quand vous
utilisez un programme écrit par quelqu’un d’autre, la conversation se
déroule entre vous et les autres programmeurs qui utilisent Python comme
intermédiaire.
Python est utilisé afin d’exprimer comment la conversation doit se
dérouler.
Pour mettre fin à une conversation avec Python, vous pouvez fermer la
fenêtre du terminal ou, de façon plus élégante, utiliser la commande quit():
>>> quit()
Interprète et compilateur
Python est un langage de haut niveau conçu afin d’être simple à lire et à
écrire pour les humains et simple à lire et à traiter pour les computer. Les
autres langages de haut niveau sont Java, C++, PHP, Ruby, Basic, Perl,
JavaScript et bien d’autres. Le hardware à l’intérieur de la Central
Processing Unit (CPU) ne comprend aucun de ces langages de haut niveau ;
en fait, CPU comprend un langage nommé langage machine. Le langage
machine est vraiment simple et franchement vraiment ennuyeux à écrire
parce qu’il n’est représenté que par des zéros et des uns qui rappellent
tellement la Matrice :
001010001110100100101010000001111
11100110000011101010010101101101
>>> y = x * 4
>>> print(y)
20
Un programme
La définition d’un programme, dans sa forme la plus facile, est une
séquence d’instructions Python qui a été créée afin de résoudre un
problème. Même une seule instruction print peut être vue comme un
programme. C’est un programme d’une ligne et pas nécessairement utile,
mais dans la définition la plus stricte, c’est un programme Python. Il peut
être plus simple de comprendre ce qu’est un programme en pensant au
problème pour lequel le programme a été conçu, puis en considérant que le
programme aide à résoudre ce problème.
Supposons que vous fassiez des recherches sur les post de Facebook et
que vous soyez intéressé par le mot le plus souvent utilisé dans une série de
post. Vous pourriez imprimer le flux de post de Facebook et scanner le texte
à la recherche du mot le plus fréquent, mais cela prendrait beaucoup de
temps et serait source d’erreurs. Vous pourriez écrire un programme Python
afin d’effectuer cette tâche avec rapidité et précision, mais surtout, afin de
pouvoir terminer cette analyse rapidement.
Imaginez devoir effectuer cette tâche en analysant des millions de lignes
de texte. Honnêtement, il serait plus simple et plus efficace pour vous
d’apprendre Python et d’écrire un programme afin de compter les mots que
de scanner l’ensemble des mots manuellement. Vous n’avez pas besoin de
centaines de lignes pour un programme aussi simple, vous en avez besoin
de moins de 20. Ne pensez-vous pas que c’est possible ? C’est ici dans
l’exemple suivant :
nomFile = input('Insérer le file:')
file = open(nomFile, 'r')
compteur = dict()
bigcompt = None
bigmot = None
for mot, compt in
list(compteur.items()) :
if bigcompt is None or compt > bigcompt:
bigmot = mot
bigcompt = compt
print(bigmot, bigcompt)
Cela semble presque trop facile pour être vrai, et bien sûr, ce n’est
jamais aussi facile. L’art d’écrire un programme consiste à composer et à
entrelacer ces éléments de base, encore et encore, afin de produire quelque
chose d’utile pour vos utilisateurs. Le programme de comptage des mots de
l’exemple ci-dessus utilise directement tous ces modèles sauf un, et à la fin
du livre, vous serez en mesure de trouver lequel.
2
VARIABLES ET EXPRESSIONS
U ne valeur est l’un des éléments de base avec lesquels un programme est
en mesure de travailler, par exemple une lettre ou un chiffre. Jusqu’à
maintenant, nous n’avons vu que des nombres et des chaînes de caractères
(séquences de caractères) comme valeurs, celles-ci appartenant à des types
différents : les nombres entiers et les chaînes de caractères. Vous (et
l’interpréteur) pouvez identifier les chaînes de caractères parce qu’elles sont
entourées de guillemets (simples ou doubles) et peuvent être imprimées
avec l’instruction print, qui fonctionne aussi pour les nombres entiers. Nous
utilisons la commande python3 afin de démarrer l’interpréteur.
python3
>>> print(4)
4
Si vous voulez savoir quel type a une valeur, l’interprète peut vous aider
comme suit :
>>> type('Hello World!')
<class 'str'>
>>> type(19)
<class 'int'>
Notez que toute valeur placée entre guillemets est en fait évaluée comme
une chaîne de caractères comme illustrée ci-dessous :
>>> type('13')
<class 'str'>
>>> type('1.2')
<class 'str'>
De même, quand vous tapez un vraiment grand nombre entier, vous pouvez
être tenté par l’utilisation de virgules séparant les groupes de trois chiffres,
comme dans 1,000, 000. Ce n’est pas un entier valide en Python, mais il ne
retournera pas d'erreur :
>>> print(1,000,000)
100
Ce n’est pas du tout ce que nous attendions, parce que Python interprète
1,000,000 comme une séquence d’entiers séparés par une virgule.
Honnêtement, il aurait été mieux de renvoyer une erreur, mais c’est le
premier exemple d'une erreur sémantique, cela signifie que le code
s’exécute sans produire de message d’erreur, mais ne fait pas ce que nous
souhaitons.
Variables
L’une des caractéristiques les plus fondamentales d’un langage de
programmation est la possibilité de manipuler des variables. Il faut noter
qu’une variable est un nom qui fait référence à une valeur. Une instruction
d’affectation crée de nouvelles variables et leur fournit des valeurs :
>>> message = 'Welcome to Python'.
>>> number = 1522
>>> piGreek = 3.1415926535897931
Opérateurs
Les opérateurs sont des symboles spéciaux qui représentent des calculs
arithmétiques comme l’addition et la soustraction. Les valeurs auxquelles
l’opérateur est appliqué sont nommées opérandes.
Les opérateurs +, -, *, / et ** effectuent respectivement l’addition, la
soustraction, la multiplication, la division et l’exposant, comme dans les
exemples illustrés ci-dessous :
20 + 32
hour - 1
hour * 60 + minutes
minutes / 60
5 ** 2
(5+9) * (15-7)
Il est essentiel de noter que l’opérateur modulo, quant à lui, fonctionne sur
des entiers et renvoie le reste du premier opérande divisé par le second. En
Python, l’opérateur modulo est caractérisé par le signe de pourcentage (%).
La syntaxe est la même que pour les autres opérateurs. Observez l’exemple
ci-dessous :
>>> quotient = 7 / 3
>>> print(quotient)
2,33333333
>>> rest = 7 % 3
>>> print(rest)
1
Attention, si l’utilisateur tape autre chose qu’un nombre, vous aurez une
erreur parce que la fonction int() ne se traduit pas comme prévu. Nous
observerons la manière de gérer les exceptions au Chapitre 3. Plus les
programmes sont volumineux et difficiles, plus ils sont compliqués à lire.
Les langages formels sont denses et il est souvent difficile d’analyser un
morceau de code et de saisir ce qui est fait ou pourquoi.
Voilà pourquoi il est une bonne idée d’ajouter des commentaires aux
programmes afin d’expliquer en langage naturel ce que fait le programme
ou d’expliquer pourquoi. Les commentaires en Python commencent par le
symbole #:
# Je cherche le pourcentage de ressources utilisées
percentage_disk = (use_disk * 100) / 60
Dans cet exemple ci-dessus, le commentaire a été placé sur une nouvelle
ligne, mais peut aussi être placé sur la même ligne que l’instruction.
L’interpréteur Python ignorera tout ce qui se trouve après la marque de
hachage jusqu’à la fin de la ligne. Les commentaires sont très utiles pour
documenter les caractéristiques non évidentes du code. Il est raisonnable de
penser que le lecteur peut comprendre ce que fait le code ; il est beaucoup
plus utile d’expliquer pourquoi. Il est inutile d’expliquer des affectations
faciles ; il est plus judicieux d’expliquer une expression difficile (par
exemple, une formule mathématique) en langage naturel.
Choisissez des noms de variables appropriés parce qu’ils peuvent
réduire le besoin de commentaires. Toutefois, les noms de variables longs
peuvent en fait rendre les expressions complexes difficiles à lire, il est alors
crucial de trouver un compromis.
3
EXÉCUTION CONDITIONNELLE
En fait, True et False sont des valeurs spéciales qui appartiennent à la classe
bool ; en fait, il faut noter que ce ne sont pas des chaînes de caractères :
>>> type(True)
<class 'bool'>
>>> type(False)
<class 'bool'>
if
Afin d’écrire des programmes utiles, nous avons presque toujours besoin de
la capacité de vérifier les conditions et de changer le comportement du
programme en conséquence. Les instructions conditionnelles nous offrent
cette possibilité et la forme la plus simple est l’utilisation de l’instruction if
:
if x > 0 :
print('J'utilise x parce qu'il est
positif')
if x > 0 :
print('J'utilise x pour mes
calculs')
if x < 0 :
# Les valeurs négatives doivent être
traitées
pass
Quand vous utilisez l’interpréteur Python, une ligne blanche doit être
laissée à la fin d’un bloc, sinon Python renverra une erreur de syntaxe
similaire à la suivante:
>>> if 10 > 0:
... print('a')
File "<stdin>", line 2
print('a')
^
IndentationError : expected an indented block
if...else
Une deuxième forme de l’instruction if est l’exécution alternative, dans
laquelle il existe deux possibilités et la condition détermine l’action à
exécuter. La syntaxe est similaire à la suivante :
if x > 0 :
print('x est supérieur à 0')
else :
print('x est inférieur ou égal à 0')
if...elif...else
Quelquefois, il y a plus de deux possibilités et nous avons besoin de plus de
deux branches. Supposons que nous voulions spécialiser davantage notre
exemple : nous souhaitons savoir quand x est égal à 0 parce qu’il ne suffit
pas de savoir qu’il est inférieur ou égal à 0.
if x < 0:
print('x est inférieur à 0')
elif x > 0:
print('x est supérieur à 0')
else:
print('x est égal à 0')
elif est l'abréviation de "else if", fréquemment utilisée dans plusieurs autres
langues. Là encore, une seule branche sera exécutée et il est important de se
souvenir qu’il n’y a pas de limite au nombre d’instructions elif. S’il y a une
clause else, elle doit être placée à la fin, mais elle ne doit pas
nécessairement s’y trouver.
Chacune des conditions est vérifiée dans l’ordre décrit. Si le premier est
faux, le suivant est vérifié et ainsi de suite. Si l’une des conditions est vraie,
la branche correspondante est exécutée et l’instruction se termine. Il est
fondamental de savoir que si plus d’une condition est vraie, seule la
première branche vraie sera exécutée ; si vous souhaitez exécuter les autres
aussi, utilisez simplement if.
Exceptions
Plus tôt, nous avons observé un segment de code dans lequel nous avons
utilisé les fonctions input et int() afin de lire et d’analyser un nombre entier
saisi par l’utilisateur. Nous avons aussi vu combien il pouvait être
dangereux de faire cela parce qu’une erreur pouvait être générée et il est
crucial de concevoir votre code afin de gérer les erreurs et les exceptions.
>>> question = 'Quel âge as-tu?\n'
>>> années = input(question)
Quel âge avez-vous?
test
>>> int(années)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: 'test'
FONCTIONS
Le nom de la fonction est type alors que l’expression entre parenthèses est
nommée l’argument de la fonction. L’argument est une valeur ou une
variable que nous passons à la fonction comme input pour la fonction elle-
même. Le résultat, pour le fonction type, est le type de l’argument. Il est
commun de dire qu’une fonction « accepte » un argument et « renvoie » un
résultat, aussi nommé valeur de retour (ou valeur retournée).
Python donne un certain nombre de fonctions intégrées essentielles que
nous pouvons utiliser sans avoir besoin d’intégrer des bibliothèques ou de
les recréer. Les créateurs de Python ont écrit un certain nombre de fonctions
afin de résoudre des problèmes courants et les ont incluses dans Python
pour nous permettre de les utiliser. Les fonctions max et min, par exemple,
renvoient respectivement la plus grande et la plus petite valeur d’une liste
ou d’une chaîne de caractères :
>>> max('abcdefgh')
'h'
>>> min('abcdefgh')
'a'
Pour les mêmes input, la majorité des programmes pour computer génèrent
les mêmes output toutes les fois. Nous disons donc qu’ils sont
déterministes. Le déterminisme est en général une excellente chose
puisque nous nous attendons à ce que le même calcul produise le même
résultat à chaque fois. Pour quelques applications, cependant, nous voulons
que le computer soit imprévisible. Les jeux en sont un exemple évident,
mais il en existe d’autres, comme la cryptographie.
Il n’est pas si simple de rendre un programme réellement non
déterministe, mais il existe des moyens vraiment utiles. L’une d’entre elles
consiste à utiliser des algorithmes qui génèrent des nombres pseudo-
aléatoires. Les nombres pseudo-aléatoires ne sont pas vraiment aléatoires
parce qu’ils sont générés par un calcul déterministe, mais en observant de
tels nombres, il est presque impossible de les distinguer des nombres
aléatoires. Le module random donne des fonctions qui génèrent des
nombres pseudo-aléatoires. La fonction random renvoie un float aléatoire
entre 0,0 et 1,0 (y compris 0,0, mais pas 1,0). Toutes les fois que vous
appelez cette fonction, vous avez le numéro suivant dans une longue série.
Pour voir un exemple, exécutez cette boucle :
Comme vous l’avez probablement deviné, def est un mot clé indiquant une
définition de fonction et le nom de la fonction est saluezUtilisateur. Les
règles applicables aux noms de fonctions sont les mêmes que pour les noms
de variables : les lettres, les chiffres et quelques signes de ponctuation sont
autorisés, toutefois le premier caractère ne peut être un chiffre. Ainsi, vous
ne pouvez pas utiliser un mot-clé comme nom d’une fonction. De plus,
vous devez éviter d’avoir une variable et une fonction portant le même
nom.
Il est important de savoir que les parenthèses après le nom de la
fonction servent à indiquer les arguments de la fonction, si elles sont vides,
elles mentionnent que la fonction n’accepte aucun argument. Dans ce cas, la
fonction accepte un paramètre d’entrée, à savoir le nom de l’utilisateur à
saluer.
La première ligne de la définition de la fonction est nommée l’en-tête ;
le reste est nommé le corps. L’en-tête doit se terminer par un deux-points et
le corps du texte doit être indenté. Par convention, l’indentation est toujours
de quatre espaces. Le corps peut contenir un nombre quelconque
d’instructions. En fait, notez que si vous tapez une définition de fonction en
mode interactif, l’agent utilisateur imprime des ellipses (...) afin de vous
informer que la définition n’est pas complète. Afin de terminer la fonction,
vous devez saisir une ligne vide (ce n’est pas nécessaire dans un script).
Un aspect spécial de Python est que la définition d’une fonction crée
une variable du même nom, et la syntaxe pour appeler (ou invoquer) une
fonction que nous créons est la même que celle utilisée pour les fonctions
intégrées dans Python. Après avoir défini une fonction, il est possible de
l’utiliser dans une autre fonction. Par exemple, nous serions en mesure
d’écrire une fonction afin d’accueillir l’utilisateur et envoyer un code de
vérification pour l’accès. Observez l’exemple ci-dessous :
>>> def saluezUtilisateur(nom):
... print('Bienvenue ' + nom)
...
>>> def accèsSécurisé(nom):
... saluezUtilisateur(nom)
... print('Pin envoyé')
...
>>> accèsSécurisé(nom)
Bienvenue Antonio
Pin envoyé
Les définitions de fonction sont exécutées précisément de la même
façon que les autres instructions, toutefois elles ont pour effet de créer des
objets fonctionnels. Les instructions contenues dans la fonction ne sont pas
exécutées tant que la fonction n’est pas appelée et que la définition de la
fonction ne génère pas de output. Comme on peut s’y attendre, une fonction
doit être créée avant de pouvoir être exécutée, autrement dit, la définition de
la fonction doit être exécutée avant le premier appel.
Pour s’assurer qu’une fonction est définie avant sa première utilisation,
il est essentiel de connaître l’ordre dans lequel les instructions sont
exécutées. Cet ordre est en fait nommé flux d’exécution. L’exécution
commence toujours à la première instruction du programme et les
instructions sont exécutées une par une, dans l’ordre de haut en bas. Il est
important de noter que les définitions de fonction ne modifient pas le flux
d’exécution du programme, mais elles nous rappellent que les instructions
contenues dans la fonction ne sont pas exécutées tant que la fonction n’est
pas appelée.
En fait, un appel de fonction est comme une diversion dans le flux
d’exécution en ce sens qu’au lieu de passer à l’instruction suivante, le flux
saute au corps de la fonction, exécute l’ensemble des instructions du corps
de la fonction, puis reprend là où il s’est arrêté. Cela semble plutôt facile,
jusqu’à ce que vous vous souveniez qu’une fonction peut en appeler une
autre.
Au milieu d’une fonction, le programme peut devoir exécuter des
instructions dans une autre fonction, créant ainsi une boucle complexe.
Heureusement, Python sait garder la trace de l’endroit où il se trouve. Donc,
lorsqu’une fonction est terminée, le programme reprend là où il s’est arrêté
dans la fonction qui l’a appelée. Quand il atteint la fin du programme, le
programme se termine. Dans cette optique, quand vous lisez un programme,
ne vous contentez pas de le lire de haut en bas, il est quelquefois plus
judicieux de suivre le flux d’exécution.
Certaines des fonctions intégrées, ainsi que la fonction qui vient d’être
créée, nécessitent des arguments. Quelques fonctions acceptent aussi plus
d’un argument et, au sein de la fonction, ces arguments sont affectés à des
variables nommées paramètres.
Les paramètres sont évalués avant que la fonction ne soit appelée. Il est
important de clarifier un aspect très fondamental qui peut être source de
confusion, surtout pour les débutants. Le nom de la variable que nous
passons comme argument (nom) n’a rien à voir avec le nom du paramètre
(nom). Le nom de la valeur dans l’appelant n’a pas d’importance ; dans la
fonction, le paramètre peut avoir un nom différent. Changeons l’exemple
pour rendre le concept plus clair :
>>> nom = 'Antonio'
>>> def saluezUtilisateur(nom):
... print('Bienvenue ' + nom)
...
>>> def accèsSécurisé(nom_utilisateur):
... saluezUtilisateur(nom_utilisateur)
... print('Pin envoyè')
...
>>> accèsSécurisé(nom)
Bienvenue Antonio
Pin envoyé
ITÉRATIONS
L esrépétitives
computer sont fréquemment utilisés afin d’automatiser des tâches
sans commettre d’erreurs. Effectivement, c’est une tâche que
les computer accomplissent généralement bien, alors que les personnes la
font mal. Parce que l’itération est si courante, Python donne de nombreuses
fonctionnalités du langage pour la rendre plus simple. Une forme d’itération
en Python est l’instruction while. Voici un programme simple pour donner
le « Mark, we are rolling » après un compte à rebours.
n=3
while n > 0:
print(n)
n=n-1
print('Mark, we are rolling!')
Vous pouvez presque lire l’instruction while comme si c’était de
l’anglais. En fait, elle veut dire « Jusqu’à ce que n soit supérieur à 0,
affichez la valeur de n, puis réduisez la valeur de n d’une unité ». Quand
vous arrivez à 0, quittez l’instruction while et affichez la phrase "Mark, we
are rolling!".
Plus formellement, voici le flux d’exécution d’une instruction while :
Cycle for
Quelquefois, nous souhaitons itérer sur un ensemble d’éléments comme une
liste de mots, les lignes de un file ou une liste de chiffres. Quand nous
disposons d’une liste définie d’éléments à parcourir par itération, nous
pouvons construire une boucle à l’aide d’une instruction for.
L’instruction while constitue une boucle indéfinie parce qu’elle itère
simplement jusqu’à ce qu’une condition devienne fausse, tandis que le
cycle for itère sur un ensemble d’éléments déjà connus de façon à parcourir
tous les éléments de l’ensemble. Effectivement, la syntaxe d’une boucle for
est similaire à celle de la boucle while en ce sens qu’elle possède un en-tête
et un corps de boucle:
utilisateurs = ['Antonio', 'Filippo', 'Marco']
for utilisateur in utilisateurs:
print('Bonjour: ', utilisateur)
print(' Bonjour à tous!')
En fait, la variable utilisateur, pour Python, est une liste de trois chaînes de
caractères et la boucle for parcourt la liste et exécute le corps de la boucle
une fois pour chacune des trois chaînes de caractères de la liste, ce qui
donne l’output illustré ci-dessous :
Bonjour: Antonio
Bonjour: Filippo
Bonjour: Marco
Bonjour à tous!
La traduction de ce for n'est pas aussi facile que dans le cas de while,
toutefois si vous considérez les utilisateurs comme un ensemble : "Exécutez
les instructions dans le corps de la boucle for une fois pour chaque
utilisateur de l’ensemble appelé utilisateurs." Les mots-clés for et in sont
réservés en Python alors que utilisateur et utilisateurs sont des variables que
nous créons.
Particulièrement, utilisateur est la variable d’itération de la boucle for.
Cette variable change pour chaque itération de la boucle et contrôle lorsque
la boucle est terminée. La variable d’itération parcourt successivement les
trois chaînes de caractères stockées dans la variable utilisateurs.
Nous utilisons fréquemment une boucle for ou while afin de parcourir
une liste d’éléments ou bien le contenu de un file et nous recherchons
quelque chose comme la plus grande ou bien la plus petite valeur dans les
données que nous parcourons. Ces boucles sont en général constituées de:
LISTES
C omme une chaîne de caractères, une liste (ou énumération) est une
séquence de valeurs. Dans une chaîne de caractères, les valeurs sont des
caractères. Il est important de noter que dans une liste, les valeurs peuvent
être de n’importe quel type et ces valeurs sont nommées éléments. Il existe
de nombreuses manières de créer une nouvelle liste ; la plus facile est de
mettre les éléments entre crochets :
[234, 212, 200, 123]
['Antonio', 'Filippo', 'Marco'].
Le premier exemple est une liste de quatre entiers alors que le second est
une liste de trois chaînes de caractères. Les éléments d’une liste peuvent
cependant ne pas être du même type. L’exemple ci-dessous illustre une liste
contenant une chaîne, un float, un entier et une autre liste:
['test', 6.3, 100, [10, 20]]
Nous avons créé une liste avec une liste à l’intérieur. En fait, cette liste peut
contenir des éléments ou être vide. Une liste qui ne contient aucun élément
est nommée liste vide ; vous êtes en mesure d’en créer une avec des
crochets vides [].
La syntaxe afin d’accéder aux éléments d’une liste est la même que
pour accéder aux caractères d’une chaîne de caractères, ce qui veut dire que
l’utilisation de l’opérateur crochet. L’expression entre parenthèses spécifie
l’index de l’élément que nous voulons sélectionner. Il faut se souvenir que
les index débutent par 0 :
>>> utilisateurs = ['Antonio', Filippo', 'Marco']
>>> print(utilisateurs[0])
Antonio
Vous pouvez considérer une liste comme une relation entre les index et les
éléments. Cette relation est nommée "mappage" ; chaque indice "mappe"
l’un des éléments. Les indices d’une liste fonctionnent de la même façon
que les indices d’une chaîne de caractères :
Cette approche fonctionne bien si vous avez seulement besoin de lire les
éléments de la liste. Cependant, si vous souhaitez écrire ou mettre à jour les
éléments, vous avez besoin d'index. Une manière fréquente de contourner
ce problème est en fait de combiner la fonction len avec range:
numéros = [10, 25, 66].
for i in range(len(numéros)):
numéros [i] = numéros[i] * 2
Cette boucle parcourt la liste et met à jour chaque élément. La fonction len
renvoie le nombre d’éléments de la liste alors que range renvoie une liste
d’indices de 0 à n-1, où n est la longueur de la liste. À chaque fois, à travers
la boucle, nous obtenons l’indice du prochain élément. L’instruction
d’affectation dans le corps de la boucle utilise i pour lire l’ancienne valeur
de l’élément et pour affecter la nouvelle valeur.
Vous pouvez vraiment tout exécuter avec les listes. Par exemple, vous
pouvez les concaténer avec l’opérateur +, vous pouvez aussi répéter des
éléments avec l’opérateur * ou même les "couper" avec l’opérateur deux
points (:).
>>> a = [1, 2, 3]
>>> b = [4, 5, 6]
>>> c = a + b
>>> print(c)
[1, 2, 3, 4, 5, 6]
>>> [0] * 4
[0, 0, 0, 0]
>>> [1, 2, 3] * 2
[1, 2, 3, 1, 2, 3]
Tout comme il est possible d’ajouter des éléments à une liste, il est aussi
possible de les supprimer avec les méthodes pop(), del et remove().
Si vous connaissez l’index de l’élément à retirer, vous pouvez utiliser
pop() qui change la liste et renvoie l’élément retiré. Si vous n’avez pas
besoin du dernier élément retiré, vous pouvez utiliser del, alors que si vous
connaissez la valeur à retirer, mais pas sa position, vous pouvez utiliser
remove(). Observons-les en action dans l’exemple ci-dessous:
>>> t = ['a', 'b', 'c']
>>> x = t.pop(1)
>>> print(t)
['a', 'c']
>>> print(x)
b
Il est important de noter que la fonction sum() ne fonctionne que lorsque les
éléments de la liste sont des nombres, tandis que les autres fonctions
(max(), len(), etc.) fonctionnent aussi avec des listes de chaînes de
caractères et d’autres types qui peuvent être comparables.
7
DICTIONNAIRES
U nlesdictionnaire est comme une liste, mais plus générale. Dans une liste,
positions d’index doivent être des entiers tandis que dans un
dictionnaire, les index peuvent être (presque) de n’importe quel type. En
fait, vous pouvez considérer un dictionnaire comme une correspondance
entre un ensemble d’index (nommés clés) et un ensemble de valeurs, où
chaque clé est associée à une valeur.
L’association d’une clé et d’une valeur est nommée paire clé-valeur ou
parfois élément. Par exemple, nous allons créer un dictionnaire qui sera en
mesure de faire correspondre des chiffres anglais à des chiffres italiens, les
clés et les valeurs seront donc toutes des chaînes de caractères. La fonction
dict crée un nouveau dictionnaire sans éléments, et comme dict est le nom
d’une fonction intégrée, il est recommandé d’éviter de l’utiliser comme
nom de variable.
>>> engToit = dict()
>>> print(engToit)
{}
Notez que de cette manière, nous avons créé un dictionnaire vide, puisque
les accolades sans valeurs à l’intérieur mentionnent de manière précise un
dictionnaire sans association. Nous pouvons ajouter des éléments comme
suit :
>>> engToit['one'] = 'un'
>>> print(engToit)
{'one' : 'un'}
Nous avons créé un élément qui est mappé de la clé one à la valeur un. En
imprimant à nouveau le dictionnaire, nous observons une paire clé-valeur
avec deux points séparant la clé de la valeur.
Tentons d’ajouter d’autres paires au dictionnaire :
>>> engToit = {'one' : 'un', 'two' : 'deux', 'three': 'trois'}
>>> print(engToit)
{'one': 'un', 'three': 'trois', 'two' : 'deux'}
for c in text:
if c not in d:
d[c] = 1
else:
d[c] = d[c] + 1
print(d)
ENSEMBLES
PROGRAMMES PYTHON
Dans cet exemple ci-dessus, les déclarations sont toutes des affectations.
Les autres déclarations valables sont les appels de fonctions, les
importations de modules ou les définitions. Les définitions comprennent les
fonctions et les classes. Les diverses structures de contrôle décrites dans les
sections suivantes s’appliquent aussi.
Python est un langage structuré par blocs et les blocs de code sont
affichés par le niveau d’indentation. La quantité d’indentation est assez
flexible, et bien que la majorité des programmeurs Python s’en tiennent à
l’utilisation de trois ou quatre espaces afin d’optimiser la lisibilité, cela ne
fait aucune différence en Python. Les divers environnements de
développement intégrés (nommés IDE) et editor de texte ont leurs propres
idées sur la façon d’effectuer l’indentation. Si plusieurs outils de
programmation sont utilisés, des erreurs d’indentation peuvent être
signalées parce que les outils ont utilisé différentes combinaisons de
tabulations et d’espaces au fil du temps.
Si vous êtes en mesure de le faire, configurez votre editor pour qu’il
utilise des espaces. Les commentaires constituent l’exception à la règle de
l’indentation. Un commentaire Python commence par le symbole # et se
poursuit jusqu’à la fin de la ligne. Python accepte les commentaires qui
commencent n’importe où sur la ligne, quel que soit le niveau d’indentation
actuel. Cependant, par convention, les programmeurs ont tendance à
maintenir le niveau d’indentation, même pour les commentaires. Python
supporte un ensemble limité d’options de sélection. La structure la plus
élémentaire est la construction if/elif/else que nous avons déjà analysée.
Il est fréquemment possible de rencontrer des erreurs dans les
programmes ; il existe deux approches de la détection des erreurs en
Python. La première consiste à vérifier explicitement chaque action au fur
et à mesure qu’elle est exécutée. Tandis que l’autre tente des opérations et
s’appuie sur le système afin de générer une condition d’erreur, ou
exception, si quelque chose ne va pas. Même si la première approche est
appropriée dans certaines situations, il est beaucoup plus fréquent dans
Python d’utiliser la seconde. Python supporte cette technique avec la
construction try/except/else/finally. Dans sa forme générale, il ressemble à
ceci :
try:
A block of application code
except <an error type> as <anExceptionObject>:
A block of error handling code
else:
Another block of application code
finally:
A block of clean-up code
Notez que except, else et finally sont tous facultatifs, bien qu’au moins un
except ou finally doive exister si une instruction try est utilisée. Il peut y
avoir plus d’une clause except, mais une seule clause else ou finally. Il est
possible d’omettre la partie as… d’une ligne d’instruction except si les
détails de l’exception ne sont pas obligatoires.
Le bloc try est exécuté et, si une erreur se produit, la classe d’exception
est testée. Si une instruction except existe pour ce type d’erreur, le bloc
correspondant est exécuté. S’il existe de nombreux blocs d’exception
spécifiant le même type d’exception, seule la première clause
correspondante est exécutée. Dans le cas où aucune instruction except
correspondante n’est trouvée, l’exception est propagée vers le haut jusqu’à
ce que l’interpréteur de niveau supérieur soit atteint et que Python génère le
rapport d’erreur de traceback habituel.
Remarquez qu’une instruction except vide permet de détecter n’importe
quel type d’erreur ; toute, c’est en général une mauvaise idée parce qu’elle
masque l’apparition de toute erreur inattendue.
Le bloc else est exécuté si le bloc try réussit sans erreur ; en pratique,
else est rarement utilisé. Qu’une erreur soit détectée ou propagée, ou que la
clause else soit exécutée, la clause finally sera toujours exécutée, donnant
alors la possibilité de libérer toute ressource de calcul dans un état bloqué.
C’est aussi le cas quand la clause try/except est liée à une instruction break
ou return.
Il est possible d’utiliser une seule instruction except afin de traiter
plusieurs types d’exception. Vous pouvez le faire en énumérant les classes
de except dans un tuple (les parenthèses sont obligatoires). L’objet
exception contient les détails de l’endroit où l’exception s’est produite et
donne une méthode de conversion de la chaîne de caractères de sorte qu’un
message d’erreur significatif puisse être donné en imprimant l’objet. Vous
pouvez aussi lever des exceptions à partir de votre code. Vous pouvez aussi
utiliser l’un des types d’exception existants ou définir les vôtres en créant
une sous-classe de la classe Exception. Vous pouvez aussi passer des
arguments aux exceptions levées et vous pouvez y accéder dans l’objet
exception de la clause exclude en utilisant l’attribut args de l’objet error.
Voici ci-dessous un exemple de génération d’une ValueError standard avec
un argument personnalisé, et ensuite de détection de cette erreur et
d’impression de l’argument spécifié.
>>> try:
... raise ValueError('wrong value')
... except ValueError as error:
... print (error.args)
...
('wrong value',)
Remarquez que vous n’avez pas obtenu un traceback complet, mais
seulement l’output imprimée du bloc except. Vous pouvez aussi relancer
l’exception originale après le traitement en appelant simplement raise sans
arguments.
Finalement, discutons du concept de contexte de runtime en Python. Il
s’agit en général d’une ressource temporaire quelconque avec laquelle votre
programme veut interagir. Un exemple typique serait un file ouvert ou un
thread d’exécution concurrent. Afin de gérer cela, Python utilise le mot-clé
with et un protocole de gestion du contexte. Ce protocole vous aide à
définir vos propres classes de gestion de contexte, mais vous utiliserez
principalement les gestionnaires donnés par Python. Vous utilisez un
gestionnaire de contexte en invoquant l’instruction with :
with open(filename, mode) as contextName:
process file here
Input et output
L’input et l’output de données de base sont une exigence essentielle de tout
langage de programmation. Vous devez tenir compte de la façon dont vos
programmes interagiront avec les utilisateurs et les données stockées dans
les file. Afin d’envoyer des données aux utilisateurs via stdout, vous utilisez
la fonction print(), que vous avez déjà vue de nombreuses fois. Vous
apprendrez comment contrôler l’output de manière plus précise dans cette
section.
Afin de lire les données des utilisateurs, nous utilisons la fonction
input(), qui demande un input à l’utilisateur et renvoie par la suite une
chaîne de caractères bruts à partir de stdin. La fonction print() est plus
complexe qu’il n’y paraît à première vue parce qu’elle comporte de
nombreux paramètres facultatifs. À son niveau le plus facile, vous passez
tout simplement une chaîne de caractères et print() l’affiche sur stdout suivi
d’un caractère de fin de ligne (eol). Le niveau de complexité suivant
implique le passage de données autres que des chaînes de caractères, que
print() convertit ensuite en chaîne de caractères (à l’aide de la fonction de
type str()) avant d’afficher le résultat. En augmentant un peu la complexité,
vous pouvez passer de nombreux éléments à la fois à print(), il les
convertira et les affichera séparés par un espace. Voici ci-dessous trois
éléments fixes dans le comportement de print() :
target = 66
while True :
value = input("Enter an integer between 1 and 100")
try:
value = int(value)
break
except ValueError:
print("I said enter an integer!")
if value > target:
print (value, "is too high")
elif int(value) < target:
print("too low")
else:
print("Pefect")
Dans cet exemple, l’utilisateur est invité à saisir un nombre entier dans la
plage appropriée. La valeur lue est ensuite convertie en un nombre entier à
l’aide de int(). Si la conversion échoue, une exception ValueError est levée
et le message d’erreur est indiqué. Si la conversion est réussie, vous êtes
donc en mesure d’interrompre la boucle while et procéder au test par
rapport au target, en sachant que vous avez un entier valide.
Les file de texte sont les chevaux de bataille de la programmation quand
il s’agit de sauvegarder des données, et Python prend en charge de
nombreuses fonctions afin de gérer les file de texte. Vous avez vu la
fonction open() dans les sections précédentes, qui prend un nom de file et
un mode comme arguments.
Le mode peut être l’un des suivants: r, w, rw et a pour lecture, écriture,
lecture-écriture et ajout respectivement. Il existe aussi plusieurs autres
modes utilisés moins fréquemment ainsi que certains paramètres optionnels
qui contrôlent la manière dont les données sont interprétées, voir la
documentation pour plus de détails.
Le mode r demande que le file existe ; les modes w et rw créent un
nouveau file vide (ou écrasent tout file existant avec le même nom). Le
mode a ouvre un file existant ou crée un nouveau file vide si un file avec le
nom de file spécifié n’existe pas déjà. L’objet file retourné est aussi un
gestionnaire de contexte et donc peut être utilisé dans un bloc with comme
vous l’avez vu dans la section précédente. Si un bloc with n’est pas utilisé,
vous devez fermer explicitement le file en utilisant la méthode close()
quand vous avez fini de travailler dessus, ce qui garantit que l’ensemble des
données contenues dans les buffer de mémoire sont envoyées au file
physique sur le disque. La construction with appelle automatiquement
close(), ce qui est l’un des avantages de l’utilisation de l’approche du
gestionnaire de contexte. Une fois que vous avez ouvert un objet file, vous
êtes en mesure d’utiliser read(), readlines() ou readline() selon vos besoins.
La fonction read() lit l’intégralité du contenu du file sous la forme d’une
chaîne unique, avec les caractères affichant le début d’une nouvelle ligne,
readlines() lit ligne par ligne dans une liste et les caractères de la nouvelle
ligne sont conservés, readline() lit la ligne suivante du file, en réservant la
nouvelle ligne. L’objet file est itérable, vous pouvez alors l’utiliser
directement dans une boucle for sans avoir besoin des méthodes de lecture.
Le modèle conseillé afin de lire les lignes dans un file est le modèle illustré
ci-dessous:
with open(filename, mode) as fileobject:
for line in fileobject:
# process line
Il est possible d’écrire dans un objet file à l’aide des méthodes write() ou
writelines(), qui sont les équivalents des méthodes read portant des noms
similaires. Remarquez qu’il n’existe pas de méthode writeline() afin
d’écrire une seule ligne. Si vous utilisez le mode rw, vous pouvez vous
déplacer vers un emplacement spécifique du file afin d’écraser les données
existantes. Vous êtes en mesure de trouver votre position actuelle dans le
file en utilisant la méthode tell(), et vous pouvez vous déplacer vers une
position spécifique (éventuellement celle que vous avez enregistrée avec
tell() précédemment) en utilisant la méthode seek(). La fonction seek() a de
nombreuses façons de calculer la position ; la valeur par défaut est
simplement l’offset au début du file.
Vous disposez maintenant de l’ensemble des compétences de base pour
écrire des programmes Python fonctionnels. Cependant, afin de vous
attaquer à des projets plus essentiels, vous devrez étendre vos compétences
en Python.
10
L apropres
façon la plus facile d’étendre l’utilisation de Python est d’écrire vos
fonctions. Vous êtes en mesure de les définir dans le même file
que le code qui les utilise, ou vous pouvez créer un nouveau module et
importer des fonctions depuis celui-ci. Pour le moment, vous allez créer les
fonctions et les utiliser dans le même file, en fait, vous pourriez surtout
utiliser le prompt interactive pour les exemples de cette section. L’étape
suivante de la création de récentes fonctions en Python consiste à définir
vos propres classes et à créer des objets à partir de celles-ci. Les exemples
illustrés ici sont suffisamment faciles pour que vous puissiez simplement
utiliser le prompt Python.
Les programmeurs Python utilisent fréquemment des chaînes de
documentation dans leurs programmes. Les chaînes de documentation sont
des chaînes littérales qui ne sont pas affectées à une variable et qui
respectent le niveau d’indentation auquel elles sont définies. Les chaînes de
documentation sont utilisées afin de décrire les fonctions, les classes ou les
modules. La fonction help() lit et affiche les chaînes de documentation.
Fonctions
Plusieurs types de fonctions sont disponibles en Python. Vous êtes en
mesure de définir des fonctions en Python à l’aide du mot-clé def. Le
formulaire ressemble à ce qui est affiché ci-dessous:
def functionName(parameter1, param2,...):
function block
Notez que les fonctions Python renvoient toujours une valeur. Vous pouvez
spécifier une valeur de retour explicite en employant le mot-clé return;
sinon, Python renvoie None par défaut. (Si vous verrez que des valeurs
None inattendues apparaissent dans l’output, vérifiez que la fonction en
question comporte une déclaration return explicite dans son corps). Vous
pouvez fournir des valeurs par défaut aux paramètres en faisant suivre le
nom d’un signe égal et de la valeur.
Dans ce cas, nous créons une nouvelle fonction qui accepte de
nombreux paramètres de input et renvoie une valeur. Cette fonction utilise
aussi l’équation mathématique d’une ligne afin de retourner la coordonnée
y correspondante pour un gradient, une coordonnée x et une constante
donnés. Nous allons alors utiliser la fonction afin de générer un ensemble
de coordonnées pour une ligne.
>>> help(straight_line)
Help on function straight_line in module __main__:
straight_line(gradient, x, constant)
returns y coordinate of a straight line
-> gradient * x + constant
(END)
Dans cette fonction, nous vérifions tout d’abord que l’argument de départ
est un nombre entier impair (un nombre entier pair divisé par 2 a un reste
nul), et si ce n’est pas le cas, nous le forçons à être le nombre entier impair
immédiatement supérieur en ajoutant 1 unité. Nous créons par la suite une
boucle infinie de type while. Normalement, c’est une mauvaise idée parce
que votre programme va se planter à tout jamais. Toutefois, comme il s’agit
d’une fonction de générateur, vous utilisez yield afin de renvoyer la valeur
de départ, de sorte que la fonction se termine à ce moment-là, en renvoyant
la valeur de départ à ce moment-là. La prochaine fois que la fonction est
appelée, l’exécution commence exactement là où vous vous êtes arrêté. Par
la suite, start est incrémenté de 2 unités et la boucle s’exécute à nouveau,
produisant le prochain nombre impair et quittant la fonction jusqu’à la
prochaine fois. Python fait en sorte que les fonctions génératrices
deviennent des itérateurs pour qu’elles puissent être utilisées dans des
boucles for, comme ceci :
for n in odds():
if n > 7: break
else: print(n)
Nous utilisons odds() comme une collection. Chaque fois que le loop est
exécutée, elle appelle la fonction générateur et reçoit la prochaine valeur
impaire. Remarquez que nous évitons une boucle infinie en insérant le test
d’interruption pour ne jamais appeler odds() au-delà de 7. Maintenant que
vous comprenez le fonctionnement des fonctions de générateur, vous avez
probablement réalisé que les expressions de générateur illustrées plus tôt
dans ce chapitre ne sont en fait que des fonctions de générateur anonymes.
Les expressions de générateur sont en fait une forme déguisée d’une
fonction de générateur sans nom. Cela constitue une transition parfaite vers
le dernier type de fonction que nous allons apprendre ici : la fonction
lambda.
Le terme lambda provient d’une forme de calcul mathématique inventée
par Alonzo Church. La excellente nouvelle, c’est qu’il n’est pas nécessaire
de s’y connaître en mathématiques afin d’utiliser les fonctions lambda !
L’idée derrière une fonction lambda est qu’il s’agit d’un bloc de fonction
anonyme, généralement vraiment petit, que vous pouvez insérer dans votre
code, qui appelle par la suite la fonction lambda comme une fonction
normale. Les fonctions lambda ne sont pas vraiment répandues, mais elles
sont très utiles quand vous devez créer de nombreuses petites fonctions qui
ne sont utilisées qu’une seule fois. Ils sont fréquemment utilisés dans des
contextes de programmation GUI ou de réseau, où le toolkit de
programmation exige qu’une fonction soit appelée avec un résultat. Une
fonction lambda est définie de la façon suivante:
lambda <param1, param2, ...,paramN> : <expression>
class MyClass(object):
instance_count = 0
def __init__(self, value):
self.__value = value
MyClass.instance_count += 1
print("instance No {} created".format(MyClass.instance_count))
def aMethod(self, aValue):
self.__value *= aValue
def __str__(self):
return "A MyClass instance with value: " + str(self.__value)
def __del__(self):
MyClass.instance_count -= 1
class Circle1:
def __init__(self, radius):
self.__radius = radius
def setRadius(self,newValue):
if newValue >= 0:
class Circle1:
def __init__(self, radius):
self.__radius = radius
def setRadius(self,newValue):
if newValue >= 0:
self.__radius = newValue
else: raise ValueError("Value must be positive")
def area(self):
return 3.14159 * (self.__radius ** 2)
class Circle2:
def __init__(self, radius):
self.__radius = radius
def __setRadius(self, newValue):
if newValue >= 0:
self.__radius = newValue
else: raise ValueError("Value must be positive")
radius = property(None, __setRadius)
@property
def area(self):
return 3.14159 * (self.__radius ** 2)
1. Enregistrer le code
2. Lancez l’interpréteur Python et tapez le code ci-dessous pour
utiliser Circle1 :
>>> c2 = Circle2(42)
>>> c2.area
5541.76476
>>> print(c2.radius)
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
AttributeError: unreadable attribute
>>> c2.radius = 12
>>> c2.area
452.38896
>>> c2.radius = ‐4
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
File "D:\PythonCode\Chapter1\testCircle.py", line 18, in __setRadius
else: raise ValueError("Value must be positive")
ValueError: Value must be positive
>>>
Notez que dans testCircle.py, vous avez créé deux classes. En effet, la
première, Circle1, réalise ce que vous souhaitiez faire en forçant
l’utilisateur à changer la valeur du rayon via la méthode setRadius(). Vous
avez fait cela en préfixant l’attribut self.__radius avec deux underscores, ce
qui est la manière dont Python le rend privé. Vous avez par la suite créé la
méthode setRadius(), qui valide la valeur fournie avant de l’appliquer et
génère une erreur si une valeur négative est trouvée. Vous avez aussi donné
une méthode area() afin que l’utilisateur puisse évaluer la zone en utilisant
la technique habituelle d’appel de méthode. La deuxième classe, Circle2,
aborde les choses de façon assez différente puisqu’elle utilise la fonction de
définition de propriété de Python pour créer un attribut appelé radius qui est
en écriture seule. Il a aussi créé la méthode area comme un attribut en
lecture seule. Cela a rendu le code utilisateur de Circle2 beaucoup plus
intuitif, comme vous avez pu le voir en utilisant le code dans l’interpréteur.
La clé se trouve dans la fonction property() que vous avez appelée de la
manière suivante :
radius = property(None, __setRadius)
Modules et package
Les modules sont essentiels dans la majorité des environnements de
programmation pour les tâches de programmation non triviales.
Effectivement, ils permettent de diviser les programmes en blocs gérables et
donnent un mécanisme de réutilisation du code entre les projets.
En Python, les modules sont tout simplement des file sources, se
terminant par .py et situés à un endroit où Python peut les trouver. En
pratique, cela veut dire que le file doit se situer dans le directory de travail
actuel ou dans un dossier répertorié dans la variable sys.path. Notez que
vous pouvez ajouter vos dossiers à ce chemin en les spécifiant dans la
variable d’environnement PYTHONPATH de votre système ou
dynamiquement au moment de l’exécution. Même si les modules
constituent un moyen utile de regrouper de petites quantités de code source
en vue de leur réutilisation, ils ne sont pas totalement satisfaisants pour les
projets de plus grande envergure comme framework GUI ou les
bibliothèques de fonctions mathématiques. Pour cela, Python fournit le
concept de paquet ou package. Un paquet est principalement un dossier
rempli de modules. L’unique exigence est que le dossier possède un file
nommé __init__.py, qui peut être vide. Pour l’utilisateur, un paquet
ressemble beaucoup à un module, et les sous-modules du paquet peuvent
ressembler à des attributs du module.
Accédez aux modules en utilisant le mot-clé import, qui a beaucoup de
variantes en Python. Les formes les plus courantes sont illustrées ci-
dessous:
import aModule
import aModule as anAlias
import firstModule, secondModule, thirdModule...
from aModule import anObject
from aModule import anObject as anAlias
from aModule import firstObject,secondObject, thirdObject...
from aModule import *
seul le module os.path est exposé. Si vous souhaitez utiliser les fonctions du
module os, vous aurez besoin d’une importation explicite. Bien que seul le
chemin soit exposé, en tant que nom de pth, les file os et path__init__.py
seront toujours exécutés. La bibliothèque standard de Python contient de
nombreux paquets, dont le paquet os que nous venons de mentionner. Parmi
les autres, mentionnons les framework d’interface utilisateur tkinter et
curses, le paquet email et les paquets urllib, http et html axés sur le Web.
Maintenant que vous avez lu la théorie, il est temps de la mettre en
pratique. Dans cette section, nous allons créer certains modules et les
regrouper dans un paquet. L’intention est de donner une interface
fonctionnelle pour les opérateurs logiques et d’étendre leur champ d’action
pour inclure le test de valeurs individuelles de bit. De cette manière, vous
verrez aussi de nombreuses caractéristiques du langage Python principal qui
ont été abordées ci-dessus. Les modules développés ne sont pas optimisés
pour la performance, mais sont conçus pour illustrer les concepts.
Cependant, il ne serait pas difficile de les affiner pour en faire des outils
réellement utiles. Nous débutons par créer un module simple et
conventionnel basé sur input entières, par la suite nous créons un autre
module qui définit une classe pouvant être utilisée afin de représenter un
morceau de données binaires et exposer des fonctions bit à bit comme
méthodes. Finalement, nous aurons besoin d’un paquet contenant les deux
modules.
#! /bin/env python3
''' Functional wrapper around the bitwise operators.
Designed to make their use more intuitive to users not
familiar with the underlying C operators.
Extends the functionality with bitmask read/set operations.
The inputs are integer values and
return types are 16 bit integers or boolean.
bit indexes are zero based
Functions implemented are:
NOT(int) -> int
AND(int, int) -> int
OR(int,int) -> int
XOR(int, int) -> int
shiftleft(int, num) -> int
shiftright(int, num) -> int
bit(int,index) -> bool
setbit(int, index) -> int
zerobit(int,index) -> int
listbits(int,num) -> [int,int...,int]
'''
def NOT(value):
return ~value
def AND(val1,val2):
return val1 & val2
def OR(val1, val2):
return val1 | val2
def XOR(val1,val2):
return val1^val2
def shiftleft(val, num):
return val << num
def shiftright(val, num):
return val >> num
def bit(val,idx):
mask = 1 << idx # all 0 except idx
return bool(val & 1)
def setbit(val,idx):
mask = 1 << idx # all 0 except idx
return val | mask
def zerobit(val, idx):
mask = ~(1 << idx) # all 1 except idx
return val & mask
def listbits(val):
num = len(bin(val)) - 2
result = []
for n in range(num):
result.append( 1 if bit(val,n) else 0 )
return list( reversed(result) )
Le module est une liste assez simple de fonctions qui encapsulent les
opérateurs bit à bit pour not (~) et (&), or (|), xor (^), shift left (<<) et shift
right (>). Notez que ces opérations fonctionnent sur des données binaires,
ce qui veut dire simplement qu’une séquence de 1 et de 0 stockée comme
une unité dans le computer. L’ensemble des données de le computer est, par
définition, stocké sous forme binaire. Ces opérations wrapper sont
complétées par une série de fonctions qui vérifient si un bit a la valeur 1
(c’est ce qu’on nomme "set"), mettent un bit (à 1) ou mettent un bit à zéro
(c’est ce qu’on nomme "reset"). Le nombre de bit est compté à partir de la
droite, en commençant par zéro. Les test sont effectués à l’aide d’un motif
de bit (aussi nommé masque de bit) qui, dans tous les cas sauf pour
zerobit(), est constitué de tous les zéros, à l’exception du bit que vous
voulez tester ou définir. Vous avez créé le masque en déplaçant 1 à gauche
du nombre de bit requis. La fonction zerobit() utilise le complément bit à bit
du masque habituel afin d’en créer un composé de tous les 1 sauf un 0 où
vous souhaitez réinitialiser le bit. Finalement, vous avez une fonction qui
liste les différents bit de la valeur donnée. Cette dernière fonction est
légèrement plus complexe et montre quelques des fonctionnalités de
Python.
Vous déterminez tout d’abord la longueur du nombre en le convertissant
en chaîne binaire avec bin() et en soustrayant 2 (pour tenir compte des
caractères 0b initiaux). Par la suite, vous créez une liste de résultats vide et
faites défiler les bit. Il est important de noter que pour chaque bit, vous
ajoutez 1 ou 0, selon que le bit est activé ou non, en utilisant la construction
d’expression conditionnelle de Python.
Le test du module soulève certaines questions intéressantes. Débutons
par importer votre nouveau module. Comme vous vous trouvez dans le
dossier où se trouve le file, Python peut le voir sans modifier la valeur
sys.path. Vous commencez à tester avec la fonction NOT() (en mode
préfixe, bien sûr, avec le nom du module, bits), et vous constatez tout de
suite une anomalie dans la mesure où l’interpréteur Python imprime la
représentation décimale comme résultat. Afin de contourner ce problème,
vous pouvez utiliser la fonction bin() pour convertir le nombre en une
représentation binaire de la chaîne de caractères. Toutefois, il est important
de savoir qu’il y a toujours un problème, car le nombre est négatif. En effet,
les entiers Python sont signés, c’est-à-dire qu’ils peuvent représenter des
nombres positifs ou négatifs. Python fait cela en interne en faisant en sorte
que le bit le plus à gauche représente le signe. En inversant tous les bit,
vous inversez aussi le signe ! Vous pouvez éviter ce problème en utilisant
un masque de bit de 0xF (ou 15 en décimal si vous préférez) pour ne
récupérer que les 4 bit les plus à droite. En convertissant ceci avec bin(),
vous observez maintenant le modèle de bit inversé que vous attendiez. Il est
évident que si la valeur que vous inversez est supérieure à 16, vous devez
utiliser un masque de bit plus long. Il est important de se souvenir que
chaque chiffre hexadécimal est composé de 4 bit. Il vous suffit alors
d’ajouter un F supplémentaire à votre masque.
Le prochain set de test, couvrant les fonctions AND() jusqu’à shiftleft(),
devrait être simple et vous pouvez vérifier les résultats en inspectant
visuellement les modèles de bit de input et les résultats. Les exemples avec
shiftright() illustrent un résultat intéressant en ce sens que décaler les bit
trop loin vers la droite produit un résultat nul. En d’autres mots, Python
remplit l’espace "vide" laissé par les opérations de décalage avec des zéros.
Concernant la nouvelle fonctionnalité, vous avez utilisé bit(), setbit() et
zerobit() afin de tester et modifier les bit individuels dans une valeur
donnée. Une fois de plus, vous pouvez inspecter visuellement les modèles
de input et de résultat afin de vérifier que les bons résultats sont produits.
Vous devez vous souvenir que le paramètre index compte à partir de zéro en
partant de la droite.
Finalement, vous avez testé la fonction listbits(). Une fois encore, vous
pouvez facilement comparer le modèle de input binaire avec la liste de
nombres qui en résulte. Vous avez maintenant un module fonctionnel que
vous pouvez importer et utiliser comme n’importe quel autre module en
Python. Notez que vous pouvez encore améliorer le module en fournissant
une fonction de test et en l’enfermant dans une clause if __name__ si vous
le voulez, mais pour l’instant vous pouvez trouver comment passer d’un
module unique à un paquet. Créez une classe qui reproduit les fonctions en
bits.py sous la forme d’un ensemble de méthodes ; ensuite, regroupez les
deux modules dans un package.
#! /bin/env python3
''' Class that represents a bit mask.
It has methods representing all
the bitwise operations plus some
additional features. The methods
return a new BitMask object or
a boolean result. See the bits
module for more on the operations
provided.
'''
class BitMask(int):
def AND(self,bm):
return BitMask(self & bm)
def OR(self,bm):
return BitMask(self | bm)
def XOR(self,bm):
return BitMask(self ^ bm)
def NOT(self):
return BitMask(~self)
def shiftleft(self, num):
return BitMask(self << num)
def shiftright(self, num):
return BitMask(self > num)
def bit(self, num):
mask = 1 << num
return bool(self & mask)
def setbit(self, num):
mask = 1 << num
return BitMask(self | mask)
def zerobit(self, num):
mask = ~(1 << num)
return BitMask(self & mask)
def listbits(self, start=0,end=-1):
end = end if end < 0 else end+2
return [int(c) for c in bin(self)[start+2:end]]
Vous avez créé une classe basée sur le type entier intégré de Python, int.
Comme vous ne donnez que de nouvelles méthodes pour la classe et que
vous ne stockez pas d’attributs de données supplémentaires, vous n’avez
pas besoin de donner un constructeur __new__() ou un initiateur __init__().
Les méthodes sont toutes vraiment similaires aux fonctions écrites dans
bits.py, mais vous avez créé une instance BitMask comme type retourné. La
méthode listbits() montre aussi une approche alternative consistant à dériver
la liste à l’aide de la représentation de chaîne bin() et à créer cette liste à
l’aide d’une conversion de caractères en nombres entiers à l’aide de int().
La fonction listbits() a aussi été étendue pour donner une paire de
paramètres de début et de fin qui, par défaut, correspondent à la longueur
complète du nombre binaire, mais peuvent aussi être utilisés afin d’extraire
un sous-ensemble de bit. Il y a un peu de travail extra selon que l’indice est
positif ou bien négatif. Notez que les index négatifs ne nécessitent pas
l’ajout de deux caractères parce qu’ils s’appliquent automatiquement à
partir de l’extrémité droite ; par conséquent, une affectation conditionnelle
Python garantit que la valeur finale correcte est définie.
Après avoir créé la classe, vous l’avez par la suite testée comme un
module standard en l’important depuis le local directory. Vous avez par la
suite répété une série de test similaires à ceux que vous avez effectués pour
bits.py. Une chose essentielle à prendre en compte : vous pouvez combiner
et assortir les opérateurs bit à bit traditionnels avec les nouvelles versions
fonctionnelles. Vous pouvez également comparer les objets BitMask
comme n’importe quel autre entier, comme vous l’avez vu dans l’exemple
shiftright(). Finalement, vous avez prouvé que votre nouvel algorithme
listbits() a fonctionné et que les nouveaux arguments supplémentaires
fonctionnent comme prévu pour les valeurs positives et négatives.
Ainsi, à ce stade, deux modules standard ont été créés dans un dossier.
Vous avez par la suite créé un file __init__.py vide qui a transformé le
dossier en paquet Python. Afin de vérifier que cela fonctionne, vous avez
changé directory pointant vers celui du haut pour que le paquet soit visible
par l’interpréteur. Une fois que vous avez confirmé que vous pouvez
importer le paquet et les modules qu’il contient, vous pouvez accéder à
quelques de ses fonctionnalités. Savoir la façon de créer et d’utiliser les
modules et paquets standard, ainsi que ceux que vous avez créés vous-
même, est un bon point de départ.
11
SCRIPT PYTHON
L’une des premières choses que nous pouvons faire lors de l’exploration
des modules de OS est de découvrir ce qu’ils peuvent nous apprendre sur
les utilisateurs. Vous pouvez notamment découvrir l’ID utilisateur, le nom
de connexion et quelques de ses paramètres par défaut. Comme la majorité
des nouvelles choses en Python, la meilleure manière de se familiariser
avec lui est par l’intermédiaire prompt interactive, alors démarrez
l’interpréteur Python et essayez-le.
>>> import os
>>> os.getlogin()
'agauld'
>>> os.getuid() # Not Windows
1001
>>> import pwd # Not Windows
>>> pwd.getpwuid(os.getuid()) # Not Windows
pwd.struct_passwd(pw_name='agauld', pw_passwd='unused',
pw_uid=1001,
pw_gid=513, pw_gecos='Alan Gauld,U-DOCUMENTATION\\agauld,
S-1-5-21-2472883112-933775427-2136723719-1001',
pw_dir='/home/agauld', pw_shell='/bin/bash')
>>> for id in pwd.getpwall():
... print(id[0])
...
SYSTEM
LocalService
NetworkService
Administrators
TrustedInstaller
Administrator
agauld
Guest
HomeGroupUser$
????????
>>>
>>> import os
>>> os.umask(0b111111111) # binary for all false ‐ 111 x 3
18
>>> bin(18)
'0b10010'
>>> os.umask(18)
511
Vous avez débuté par appeler os.umask() avec une valeur binaire de
111111111. Cela définit toutes les permissions à false, ce que vous avez fait
comme une fonction de sécurité au cas où quelque chose se passe mal. Il est
mieux d’avoir un masque trop restrictif que de laisser l’utilisateur
vulnérable aux exploit de sécurité. Python a par la suite imprimé la valeur
décimale 18, l’appel de la fonction bin() montre que 18 a la valeur de
masque binaire de 10010. Notez que si vous le remplissez de zéros afin
d’obtenir le masque complet de 9 bit et le divisez en groupes de 3 bit, vous
observerez qu’il est de 000-010-010, cette valeur représente un accès
complet au Propriétaire, mais seulement un accès en lecture et en exécution
aux utilisateurs du Groupe et du Monde. Finalement, vous avez restauré le
paramètre utilisateur original en appelant os.umask() à nouveau avec un
argument de 18 (la valeur originale retournée par umask) et la valeur de
masque précédente (111111111) que vous aviez définie a été imprimée, en
décimal, comme 511.
Quelquefois, vous souhaitez savoir quel type de système informatique
l’utilisateur utilise, en particulier vous souhaitez connaître les détails du
système d’exploitation lui-même. Python a quelques manières de le faire,
mais celle que vous allez voir en premier est certainement la propriété
os.name. Au moment de la rédaction, cette propriété renvoie l’une des
valeurs suivantes: posix, nt, mac, os2, ce ou java. Un autre endroit où
rechercher le système utilisé par l’utilisateur est le module sys et, en
particulier, l’attribut sys.platform. Cet attribut renvoie souvent des
informations légèrement diverses de celles trouvées en utilisant os.name.
Par exemple, Windows est signalé comme win32 au lieu de nt ou ce. Sous
UNIX, une autre fonction de os appelée os.uname() donne un peu plus de
détails. Si vous disposez de quelques systèmes d’exploitation, il peut être
intéressant de comparer les résultats de ces diverses techniques. Nous
conseillons d’utiliser l’option os.name simplement, car elle est
universellement disponible et qu’elle renvoie un ensemble bien défini de
résultats. Une autre information qu’il est souvent utile de recueillir est la
taille du terminal de l’utilisateur sur le plan de lignes et de colonnes. Vous
pouvez utiliser ces informations pour changer l’affichage des messages de
vos script. Le module shutil donne une fonction pour cela appelée
shutil.get_terminal_size(), et elle est utilisée de cette manière :
>>> import shutil
>>> cols, lines =
shutil.get_terminal_size()
>>> cols
80
>>> lignes
49
>>> import os
>>> os.getpid()
16432
>>> os.getppid()
3165
>>> os.getcwd()
/home/agauld
>>> len(os.environ)
48
>>> os.environ['HOME']
'/home/agauld'
>>> os.environ['testing123']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.7/UserDict.py", line 23, in __getitem__
raise KeyError(key)
KeyError: 'testing123'
>>> os.environ['testing123'] = 42
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.7/os.py", line 471, in __setitem__
putenv(key, item)
TypeError: str expected, not int
>>> os.environ['testing123'] = '42'
>>> os.environ['testing123']
'42'
>>> del(os.environ['testing123'])
>>> os.environ['testing123']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.7/UserDict.py", line 23, in __getitem__
raise KeyError(key)
KeyError: 'testing123'
Après avoir importé os dans la première ligne, la première chose à faire est
de déterminer l’ID du processus actuel. Vous êtes dans l’interpréteur
Python, c’est alors le processus propre à l’interpréteur que vous identifiez.
La prochaine ligne lit l’ID du processus parent ; dans ce cas, il s’agit du
programme shell du système d’exploitation. Vous pouvez utiliser ces ID
quand vous interagissez avec d’autres outils du système d’exploitation.
Vous avez ensuite utilisé la fonction os.getcwd() (qui signifie get current
working directory) afin de déterminer quel est la directory par défaut. Il
s’agit en général du directory à partir duquel l’interpréteur a été invoqué,
mais, comme vous le constaterez, il est possible de modifier directory dans
votre programme. La fonction os.getcwd() est un moyen utile de vérifier
l’emplacement exact de votre code à un moment donné. Par la suite, vous
avez découvert la taille de votre environnement en consultant la longueur
du dictionnaire os.environ, ensuite vous avez extrait la valeur de la variable
d’environnement HOME qui indique la home directory de l’utilisateur.
Vous avez tenté d’accéder à une variable nommée testing123, mais comme
elle n’existe pas, vous avez obtenu un KeyError. Vous avez tenté de créer
une variable testing123 en lui attribuant le nombre 42, mais cela a produit
un TypeError parce que les variables d’environnement doivent être des
chaînes de caractères. En assignant la valeur de la chaîne '42' à la variable
testing123, vous avez réussi à créer une nouvelle variable d’environnement
pour ce processus (souvenez-vous que l’environnement est local à ce
processus et à tous les sous-processus qu’il génère, il n’est pas visible pour
les autres processus externes). Vous lisez par la suite à nouveau la valeur et
obtenez cette fois une valeur sans message d’erreur, confirmant que tout
s’est bien passé. Finalement, vous avez supprimé la variable et prouvé
qu’elle avait disparu en tentant de la lire à nouveau, ce qui a entraîné,
comme prévu, une erreur.
Il est souvent utile de pouvoir exécuter d’autres programmes à partir
d’un script, et le module subprocess est l’outil privilégié pour cela. Le
module donne une classe appelée Popen qui donne une interface vraiment
puissante et flexible avec des programmes externes ; elle fournit aussi un
certain nombre de fonctions utiles que vous pouvez utiliser lorsque vous
préférez une approche plus facile. La documentation décrit comment
utiliser toutes ces fonctions ; dans cette section, seule la fonction la plus
simple, subprocess.call(), et la classe Popen sont utilisées. L’utilisation la
plus élémentaire du module consiste à appeler une commande externe du
système d’exploitation et à lui permettre simplement de suivre son cours.
Notez que l’output est en général affichée à l’écran ou stockée dans un file
de données quelque part. À la fin du programme, vous pouvez demander à
l’utilisateur de faire une sélection sur la base de ce qui a été affiché ou vous
pouvez accéder au file de données directement à partir de votre code. Il est
important de savoir qu’il est possible de forcer de plusieurs outils du
système d’exploitation, notamment sur les systèmes UNIX, à produire un
file de données en output en fournissant des options de ligne de commande
appropriées ou en utilisant la redirection de file du système d’exploitation.
Cette technique est une façon vraiment puissante d’exploiter la puissance
des outils du système d’exploitation de façon à ce que Python puisse les
utiliser pour un traitement ultérieur. Ce mécanisme de base afin d’appeler
un programme est encapsulé dans la fonction subprocess.call(). Le premier
paramètre de cette fonction est une liste de chaînes de caractères, suivie de
plusieurs paramètres facultatifs de mots-clés utilisés pour contrôler les
positions de input et de output et quelques autres éléments. La manière la
plus facile de voir comment cela fonctionne est de l’essayer.
root
fileA.txt
fileB.txt
Après avoir importé subprocess avec l’alias sub dans la première ligne (cela
évite de taper un peu plus tard !), vous appelez sub.call() pour la première
fois, avec un argument ['ls'] ou ['cmd', '/c’, 'dir', '/b'] selon votre système
d’exploitation (remarquez que dir est en fait une sous-commande du
processus shell cmd.exe, le système d’aide de Windows explique ce que
font les flag /ce /b). L’output est démontrée sur stdout (votre terminal), mais
les noms de les file ne sont pas accessibles depuis Python. La seule chose
renvoyée par call() est le code de retour du système d’exploitation, qui vous
affiche si le programme s’est terminé avec succès ou non, mais ne vous aide
en aucune manière à interagir avec les données. Vous avez par la suite
utilisé sub.call() une deuxième fois, mais cette fois vous avez redirigé
stdout vers un nouveau file: ls.txt.
Par la suite, l’outil more du système d’exploitation (ou type sur
Windows) a été utilisé pour afficher le file. Le fait que ls.txt soit un file
texte normal veut aussi dire que vous pouvez accéder aux données en
ouvrant le file et en le traitant de la façon habituelle à l’aide de commandes
Python. Dans ce cas, vous avez effectué simplement un loop sur les lignes
et les avez imprimées, toutefois, vous auriez pu stocker et utiliser les
données à d’autres fins tout aussi facilement. Il convient de noter que
l’exposition d’une liste de file dans un file de texte comme celui-ci
constitue un problème de sécurité potentiel et que vous devez supprimer le
file dès que possible après l’avoir traité.
Un souci qui peut survenir pendant l’exécution de programmes externes
est que le système d’exploitation ne trouve pas la commande. Un message
d’erreur est en général indiqué dans ce cas et il est essentiel de donner
explicitement le chemin d’accès complet de le file du programme, en
supposant qu’il existe réellement. Finalement, réfléchissez à la manière
d’interrompre un processus en cours. En ce qui concerne les programmes
interactifs, le plus facile est que l’utilisateur ferme le programme externe
normalement ou émette un signal d’abandon en utilisant Ctrl+C ou Ctrl+Z,
ou toute autre norme du système d’exploitation de l’utilisateur. Cependant,
pour les programmes non interactifs, il peut être nécessaire d’intervenir à
partir du système d’exploitation, en général, en examinant la liste des
processus en cours et en mettant explicitement fin au processus défectueux.
Vous venez de constater combien il est facile d’utiliser subprocess.call()
afin de lancer un processus externe. Observons maintenant comment le
module sous-processus vous donne un contrôle beaucoup plus crucial sur
les processus et, en particulier, comment il permet à votre programme
d’interagir avec eux pendant leur exécution, notamment la façon de lire
l’output du processus directement à partir de votre script.
Ici, vous spécifiez que stdout doit être un sub.PIPE, ensuite vous attribuez
l’attribut stdout de l’instance Popen à lsout (une pipe est simplement une
connexion de données à un autre processus, dans ce cas entre votre
programme et la commande que vous exécutez). Une fois ceci fait, vous
pouvez donc traiter la variable lsout comme un file Python normal, le lire et
ainsi de suite. Notez que vous pouvez envoyer des données au processus de
la même façon en spécifiant que stdin est une pipe sur lequel vous pouvez
par la suite écrire. Les valeurs valides que vous pouvez attribuer aux divers
flux comprennent file ouverts, les descripteurs de file ou d’autres flux (afin
que stderr puisse être affiché sur stdout, par exemple). Notez qu’il est
possible de concaténer des commandes externes en définissant, par
exemple, l’entrée du second programme comme la sortie du premier.
Remarquez que cela produit un effet similaire à l’utilisation du caractère
pipe (|) du système d’exploitation sur une ligne de commande.
Pour débuter, vous avez importé le subprocess avec l’alias sub. La première
paire de commandes reproduit tout simplement ce que vous avez exécuté
avec subprocess.call() en produisant initialement une liste de file sur stdout,
mais sans pouvoir utiliser ces données. Le deuxième cas est plus intéressant
parce que vous avez redirigé stdout vers un sub.PIPE qui vous a permis de
le lire via l’attribut stdout. Remarquez la différence entre le paramètre
stdout, que vous définissez à sub.PIPE dans l’appel du constructeur de
Popen, et l’attribut stdout que vous utilisez afin de lire les données de
l’instance Popen et qui est accessible via la dot notation.
Afin de l’utiliser, vous avez aussi affecté le résultat de l’appel Popen à
une variable appelée ls. Il est important de savoir que Popen est en fait une
classe et le résultat de l’appel est une nouvelle instance de l’objet Popen. Le
résultat final est vraiment similaire au cas du subprocess.call() où vous avez
envoyé l’output vers un file, ls.txt, et lu le file, mais dans ce cas vous ne
créez aucun file. Au contraire, vous lisez directement à partir du processus,
ce qui veut dire que vous ne finissez pas par encombrer votre file system
avec de petits file temporaires qui doivent être réorganisés ultérieurement.
Le prochain exemple utilise l’editor de ligne UNIX ex, mais cette fois vous
redirigez stdin vers sub.PIPE et entrez par la suite un certain nombre de
commandes dans l’editor pour créer un court file texte. Vous avez aussi
passé un nom de file comme argument en donnant à Popen une deuxième
chaîne de caractères dans le premier argument. Remarquez que l’input dans
stdin doit être une chaîne de byte (b'xxxx') au lieu d’une chaîne de texte
normale.
L’avant-dernier exemple illustre ce qui se passe si vous tentez d’ouvrir
un file inexistant (ou si vous ne fournissez pas les informations de chemin
correctes). Vous obtenez une exception OSError que vous pouvez, bien sûr,
attraper en utilisant la fonction try/except de Python.
Dans les exemples, vous avez accédé directement à stdin et stdout ;
toutefois, cela peut parfois poser des problèmes, notamment lors de
l’exécution simultanée de processus ou au sein de thread, ce qui entraîne le
remplissage de les pipe et le blocage des processus. Afin d’éviter ces
soucis, nous conseillons d’utiliser la méthode Popen.communicate() et
d’indexer le flux approprié. Ce moyen est légèrement plus difficile à
utiliser, mais elle permet d’éviter les soucis discutés ci-dessus. La fonction
Popen.communicate() prend une chaîne de input (équivalente à stdin) et
renvoie un tuple dont le premier élément est le contenu de stdout et le
second le contenu de stderr. Alors, la répétition de l’exemple de la liste de
les file en utilisant Popen.communicate() ressemble à ceci:
Ici vous avez vérifié la liste du directory courant ('.') avec os.listdir(),
maintenant que vous avez vu os.listdir(), j’espère que vous réalisez que
votre utilisation de ls ou dir dans subprocess était plutôt artificielle, car
os.listdir() fait le même travail directement depuis Python, et le fait plus
efficacement. Vous avez par la suite utilisé la fonction os.stat() afin
d’obtenir des informations sur l’un des file, cette fonction renvoie un objet
tuple nommé contenant 10 éléments d’intérêt. Il est important de se
souvenir que les plus utiles d’entre elles sont probablement st_uid, st_size
et st_mtime. Ces valeurs représentent l’ID utilisateur du propriétaire du file,
la taille et la date/heure de la dernière modification. Notez que les temps
sont des entiers qui doivent être décodés à l’aide du module time, comme
ceci:
>>> import time
>>> time.localtime(1388677418)
time.struct_time(tm_year=2014, tm_mon=1, tm_mday=2, tm_hour=15,
tm_min=43, tm_sec=38, tm_wday=3, tm_yday=2, tm_isdst=0)
>>> time.strftime("%Y‐%m‐%d", time.localtime(1388677418))
'2014-01-02'
Dans cet exemple, vous avez utilisé la fonction localtime() du module time
afin de convertir la valeur entière st_mtime en un tuple indiquant les valeurs
de l’heure locale et, à partir de là, en une chaîne de date lisible à l’aide de la
fonction time.strftime() avec une chaîne de format appropriée. Le tuple de
10 valeurs retourné par os.stat() est en général utile, mais plus de détails
sont disponibles via os.stat(). Il est important de savoir que certaines de ces
valeurs supplémentaires dépendent du système d’exploitation, comme
l’attribut st_obtype que l’on trouve sur les systèmes RiscOS. Vous devez
faire un peu plus de travail afin de les extraire, mais vous pouvez accéder
aux détails en utilisant la notation par points sur l’attribut de l’objet. Notez
que le champ le plus intéressant auquel vous pouvez accéder à partir de
os.stat() est sans doute la valeur st_mode, qui vous indique les autorisations
d’accès au file. Vous l’utilisez de la manière suivante :
>>> import os
>>> stats = os.stat('fileA.txt')
>>> stats.st_mode
33204
Toutefois il n’est pas très utile ; il s’agit tout simplement d’un nombre
apparemment aléatoire ! Le secret réside dans les bit individuels qui
composent le nombre. En effet, il s’agit d’un autre masque de bit. Vous
vous rappelez probablement du masque de bit umask que vous avez vu
précédemment. Le st_mode est conceptuellement similaire à umask, mais
avec la signification des bit inversée. Vous pouvez constater comment les
détails de l’accès sont encodés en observant les 9 derniers bit, comme ceci :
>>> bin(stats.st_mode)[‐9:]
'111111101'
>>> import os
>>> cwd = os.getcwd()
>>> print (cwd)
/home/agauld/book/root
>>> os.listdir(cwd)
['fileA.txt', 'fileB.txt', 'ls.txt']
>>> os.mkdir('subdir')
>>> os.listdir(cwd)
['fileA.txt', 'fileB.txt', 'ls.txt', 'subdir']
>>> os.chdir('subdir')
>>> os.getcwd()
'/home/agauld/book/root/subdir'
>>> os.chdir('..')
>>> os.getcwd()
'/home/agauld/book/root'
Après avoir importé os dans la première ligne, vous avez stocké la directory
courant dans cwd et l’avez par la suite imprimé afin de confirmer que vous
étiez là où vous pensiez être. Vous avez par la suite énuméré son contenu.
Ensuite, vous avez créé un répertoire nommé subdir et vérifié que le
déplacement a réussi en appelant os.getcwd() à nouveau depuis le nouveau
répertoire. Finalement, vous êtes retourné au directory précédent en utilisant
le raccourci '..' et avez vérifié une fois de plus que cela fonctionnait avec
os.getcwd(). Un souci avec la fonction os.mkdir() utilisée ici est qu’elle ne
peut créer une directory que dans une directory existant. Si vous tentez de
créer une directory dans un emplacement qui n’existe pas, cela échoue.
Python donne une fonction alternative nommée os.makedirs()—faites
attention à la différence d’orthographe—qui crée tous les répertoires
intermédiaires dans un chemin s’ils n’existent pas déjà. Vous pouvez
constater comment cela fonctionne avec les commandes ci-dessous:
>>> os.mkdir('test2/newtestdir')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: [Errno 2] No such file or directory: 'test2/newtestdir'
>>> os.makedirs('test2/newtestdir')
>>> os.chdir('test2/newtestdir')
>>> print( os.getcwd() )
/home/agauld/book/root/test2/newtestdir
Ici, l’appel original os.mkdir() a produit une erreur parce que le dossier
intermédiaire test2 n’existait pas. L’appel à os.makedirs() a toutefois réussi
à créer à la fois les dossiers test2 et newtestdir et vous avez pu passer à
newtestdir afin de prouver le point. Remarquez que os.makedirs() génère
une erreur si le dossier cible existe déjà. Vous pouvez utiliser certains
paramètres supplémentaires afin d’optimiser davantage le comportement,
mais les valeurs par défaut sont en général ce dont vous avez besoin. Un
autre module, shutil, donne un certain nombre de commandes de
manipulation de les file de plus haut niveau. De plus, il est notamment
possible de copier file individuels, de copier des arborescences de directory
entières, de supprimer des arborescences de directory et de déplacer file ou
des arborescences de directory entières. Une anomalie est la possibilité de
supprimer un seul file ou un groupe de file. Ceci se situe en fait dans le
module os sous la forme de la fonction os.remove() pour les file (et
os.rmdir() pour les directory vides, bien que shutil.rmtree() soit plus
puissant et généralement ce que vous souhaitez). Il est important de savoir
qu’il y a un autre module utile nommé glob. Ce module donne l’occasion de
gérer les caractères jolly de le nom file. Vous connaissez peut-être les
caractères jolly ? et * utilisés afin de spécifier des groupes de file dans les
commandes du système d’exploitation. Par exemple, *.exe spécifie
l’ensemble de les file se terminant par .exe. La fonction glob.glob() fait la
même chose dans votre code en retournant une liste de noms de file
correspondants pour un motif donné.
Utilisez les fonctions os, glob et shutil afin de manipuler file entiers.
Suivez les étapes ci-dessous :
>>> sh.copy('fileA.txt','fileX.txt')
>>> sh.copy('fileB.txt','subdir/fileY.txt')
>>> os.listdir('.')
['fileA.txt', 'fileB.txt', 'fileX.txt', 'ls.txt', 'subdir', 'test.py', 'test2']
>>> os.listdir('subdir')
['fileY.txt']
>>> sh.copytree('subdir', 'test3')
>>> os.listdir('.')
['fileA.txt', 'fileB.txt', 'fileX.txt', 'ls.txt', 'subdir', 'test.py',
'test2', 'test3']
>>> os.listdir('test3')
['fileY.txt']
>>> sh.rmtree('test2')
>>> os.listdir('.')
['fileA.txt', 'fileB.txt', 'fileX.txt', 'ls.txt', 'subdir', 'test.py', 'test3']
>>> os.mkdir('test4')
>>> sh.move('subdir/fileY.txt', 'test4')
>>> os.listdir('test4')
['fileY.txt']
>>> os.listdir('subdir')
[]
>>> os.remove('test4/fileY.txt')
>>> os.listdir('test4')
[]
>>> os.remove('test4')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: [Errno 1] Operation not permitted: 'test4'
>>> sh.rmtree('test4')
>>> sh.rmtree('test3')
>>> os.remove('fileX.txt')
>>> os.listdir('.')
['fileA.txt', 'fileB.txt', 'ls.txt', 'subdir', 'test.py']
Apprendre à programmer est difficile. Ne croyez pas ceux qui vous disent
que c’est facile. La question essentielle à se poser est « Voulez-vous
apprendre Python ? ». Ce livre vous a certainement aidé à répondre à cette
question, mais préparez-vous à lutter, à commettre des erreurs, à être frustré
et à vous taper la tête contre le clavier.
C’est maintenant à votre tour d’approfondir d’autres aspects de Python,
de découvrir des bibliothèques intéressantes, de créer des applications et
bien plus encore. Si vous aimez résoudre des soucis complexes, si vous
aimez les Sudoku, les mots croisés ou les puzzle, en d’autres mots, si vous
aimez les défis, donc vous pourriez aimer programmer en Python. Ce n’est
pas simple, mais c’est assez facile pour commencer ; vous pouvez utiliser
ce e-book chaque fois que vous souhaitez revoir des fonctions ou clarifier
des doutes.
Soyez patient avec vous-même et faites semblant d’être le professeur et
l’élève en même temps. Quand l’élève échoue, l’excellent enseignant ne
l’insulte pas, ne le réprimande pas, mais l’encourage. L’excellent enseignant
sait que l’échec fait partie de l’apprentissage et que le fait de le surmonter
est gratifiant et permet d’apprendre davantage. Alors, soyez un excellent
professeur.
Mais tentez aussi d’être un excellent étudiant. Quand vous relirez ce
livre, vous devrez être assis à côté de votre computer. Fixez-vous un
objectif, par exemple, prévoyez de passer au moins deux heures sans
interruption ou bien prévoyez de résoudre une task simple au début. Ce
processus vous fera passer par l’apprentissage de base et à partir de là, ce
sera une transition continue du livre à la visualisation, l’édition et l’écriture
du code. Nous n’apprenons pas la programmation en lisant, mais en
s’exerçant.
Quand vous avez débuté à lire ce e-book, vous n’aviez probablement
aucune connaissance préalable de Python, mais maintenant que vous
connaissez les bases, vous serez en mesure d’aborder des sujets assez
avancés comme le travail avec la programmation orientée objet, les
database ou bien l’utilisation de Python pour le Web.
Maintenant, tout est entre vos mains, vous avez une base, faites-en bon
usage.