Le langage Python

| Chris BURNS | Christophe COMBELLES | Emmanuelle GOUILLART | Gaël VAROQUAUX |

Traducteur

GALODE Alexandre

Python est un langage de programmation au même titre que le langage C, le Fortran, le BASIC, PHP…

Voici quelques-unes des fonctionnalités spécifiques de Python :

- C'est un langage interprété (par opposition aux langages compilés). Contrairement au C ou au Fortran, par exemple, on ne compile pas du code Python avant de l'exécuter. En outre, Python peut être utilisé de manière interactive : de nombreux interpréteurs sont disponibles, à partir desquels les commandes et les scripts Python peuvent être exécutés.

- C'est un logiciel gratuit placé sous licence Open Source : Python peut ainsi être utilisé et distribué gratuitement, y compris pour des applications commerciales.

- Il est multiplate-forme : Python est disponible pour la plupart des systèmes d'exploitation, Windows, Linux/Unix, MacOS X, la plupart des OS pour téléphones portables…

- C'est un langage très facile à lire, avec une syntaxe claire.

- C'est un langage pour lequel de nombreux packages de grande qualité sont valables, et ce pour de nombreuses applications, des frameworks web aux calculs scientifiques.

- C'est un langage avec lequel il est très facile de s'interfacer, en particulier depuis du langage C ou du C++.

- Plusieurs autres fonctionnalités du langage sont abordées dans ce qui suit. Par exemple, Python est un langage orienté objet, avec un typage dynamique (la même variable peut contenir des objets de différents types durant l'exécution du programme).

Consultez http://www.python.org/about/ pour plus d'informations concernant les différentes fonctionnalités de Python.

Commentez Commentez

- Titre : Le langage Python

- Auteurs : Chris BURNS, Christophe COMBELLES, Emmanuelle GOUILLART, Gaël VAROQUAUX

- Traducteur : GALODE Alexandre

- Parution : 26 septembre 2014

- Mise à jour : 26 septembre 2014

- Public visé :

- Licence : Le contenu de cet article est rédigé par GALODE Alexandre 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. Premiers pas

- II. Les types de base

- II-A. Types numériques

- II-B. Les conteneurs

- II-B-1. Listes

- II-B-2. Strings / Chaînes de caractères

- II-B-3. Dictionnaires

- III. Contrôle de flux

- III-A. if/elif/else

- III-B. for/range

- III-C. while/break/continue

- III-D. itérations avancées

- III-D-1. Itération de n'importe quelle séquence

- III-D-2. Garder une trace de l'index

- III-D-3. Boucler sur un dictionnaire

- III-E. Liste en compréhension

- III-F. Exercice

- IV. Définir des fonctions

- IV-A. Définition de fonctions

- IV-B. Effectuer un retour

- IV-C. Les paramètres

- IV-D. Transmettre une valeur

- IV-E. Les variables globales

- IV-F. Nombres variables de paramètres

- IV-G. Les docstrings

- IV-H. Les fonctions sont des objets

- IV-I. Les méthodes

- IV-J. Exercice : Suite de Fibonacci

- IV-K. Exercice : Tri rapide

- V. Réutilisation de code : scripts et modules

- V-A. Les scripts

- V-B. Importer des objets depuis des modules

- V-C. Créer des modules

- V-C-1. Introspection

- V-C-2. Import d'objet dans l'espace de nom principal

- V-D. '__main__' et chargement de modules

- V-E. Scripts ou modules : Comment organiser votre code ?

- V-E-1. Comment les modules sont trouvés et importés

- V-F. Les packages

- V-G. Bonnes pratiques

- VI. Entrées et sorties

- VI-A. Modes de fichiers

- VII. La librairie standard

- VII-A. Module OS : Fonctionnalité de l'OS

- VII-A-1. Manipulation de fichiers et de dossiers

- VII-A-2. Analyser un dossier

- VII-B. Module shutil : Opérations fichiers de haut niveau

- VII-C. Module glob : Recherche de pattern sur les fichiers

- VII-D. Module sys : Informations spécifiques du système

- VII-E. Module pickle : persistance facile

- VII-F. Exercice

- VIII. Les exceptions avec Python

- VIII-A. Les exceptions

- VIII-B. Intercepter les exceptions

- VIII-B-1. Try/except

- VIII-B-2. Try/finally

- VIII-B-3. Réduire au silence une exception

- VIII-B-4. Lever des exceptions

- IX. Programmation orientée objet (POO)

- X. Remerciements

I - Premiers pas

Démarrez le shell IPython (un shell Python avancé) :

- soit en tapant ipython depuis un terminal Linux/Mac, ou depuis cmd sous Windows ;

- soit en démarrant le programme depuis un menu, par exemple Python(x,y) ou EPD si vous avez installé une de ces suites Python pour scientifiques.

Si vous n'avez pas IPython d'installé sur votre ordinateur, sachez que d'autres shell Python sont disponibles, comme le shell Python de base que vous pouvez lancer en tapant python dans un terminal, ou encore l'interpréteur IDLE. Cependant, nous vous recommandons d'utiliser le shell IPython en raison de ses fonctionnalités avancées, spécialement en ce qui concerne le calcul scientifique interactif.

Une fois que vous avez lancé l'interpréteur, veuillez saisir :

>>> print "Hello, world!"
Hello, world!

Le message Hello, world ! est alors affiché à l'écran. Vous venez juste d'exécuter votre première instruction Python, toutes nos félicitations !

Pour continuer sur votre lancée, veuillez saisir l'ensemble des instructions suivantes :

>>> a = 3
>>> b = 2*a
>>> type(b)
<type 'int'>
>>> print b
6
>>> a*b
18
>>> b = 'hello'
>>> type(b)
<type 'str'>
>>> b + b
'hellohello'
>>> 2*b
'hellohello'

Deux variables a et b sont définies ci-dessus. Veuillez noter qu'on ne déclare pas le type d'une variable avant de lui assigner une valeur. En C, nous aurions dû écrire :

int a = 3;

De plus, le type d'une variable peut changer, comprenez que dans un premier temps, la variable peut être d'un type donné, puis que dans un second temps, la variable sera d'un type différent. Ainsi, b fut d'abord un entier, puis devint une chaîne de caractères (string), quand on lui assigna la valeur " hello ". Les opérations sur les entiers (exemple : b = 2*a) sont codées de manière naturelle en Python, de même que les opérations sur les chaînes de caractères. Ainsi l'utilisation des additions et des multiplications sur une chaîne de caractères correspondront respectivement à une concaténation et à une répétition.

II - Les types de base

II-A - Types numériques

Python supporte les types numériques et scalaires suivants :

Entiers :

>>> 1 + 1
2
>>> a = 4
>>> type(a)
<type 'int'>

Flottants :

>>> c = 2.1
>>> type(c)
<type 'float'>

Complexes :

>>> a = 1.5 + 0.5j
>>> a.real
1.5
>>> a.imag
0.5
>>> type(1. + 0j )
<type 'complex'>

Booléens :

>>> 3 > 4
False
>>> test = (3 > 4)
>>> test
False
>>> type(test)
<type 'bool'>

Un shell Python peut parfaitement remplacer votre calculatrice de poche, avec les opérateurs arithmétiques de base +, -, *, /, % (modulo), implémentés nativement.

>>> 7 * 3.
21.0
>>> 2**10
1024
>>> 8 % 3
2

Transtypage :

>>> float(1)
1.0

Division entière :

>>> 3 / 2
1

Utilisation des flottants

>>> 3 / 2.
1.5
>>> a = 3
>>> b = 2
>>> a / b
1
>>> a / float(b)
1.5

Si vous désirez une division avec un résultat entier, utilisez // :

>>> 3.0 // 2
1.0

Le fonctionnement de la division a été modifié dans Python 3. Veuillez vous reporter à http://python3porting.com/preparing.html#use-instead-of-when-dividing-integers pour plus d'informations.

II-B - Les conteneurs

Python fournit différents types de conteneurs puissants, dans lesquels nous pouvons stocker des listes ou des objets.

II-B-1 - Listes

Une liste est un jeu de données et/ou d'objets qui peuvent être de différents types. Par exemple :

>>> L = ['red', 'blue', 'green', 'black', 'white']
>>> type(L)
<type 'list'>

Indexation : accès distinct à chaque objet contenu dans la liste :

>>> L[2]
'green'

Partir de la fin avec des indices négatifs :

>>> L[-1]
'white'
>>> L[-2]
'black'

Les indices commencent à 0 (comme en C), pas à 1 (comme en Fortran ou Matlab) !

Découpage : obtenir un fragment de la liste :

>>> L
['red', 'blue', 'green', 'black', 'white']
>>> L[2:4]
['green', 'black']

Veuillez noter que L[start:stop] contient les éléments pour lesquels start <= i < stop (i va alors de start à stop-1). Autrement dit, L[start:stop] extrait (stop-start) éléments de la liste.

Syntaxe du slicing : L[start:stop:intervalle]

Tous les paramètres de l'échantillonnage sont optionnels :

>>> L
['red', 'blue', 'green', 'black', 'white']
>>> L[3:]
['black', 'white']
>>> L[:3]
['red', 'blue', 'green']
>>> L[::2]
['red', 'green', 'white']

Les listes sont des objets mutables et peuvent par conséquent être modifiées :

>>> L[0] = 'yellow'
>>> L
['yellow', 'blue', 'green', 'black', 'white']
>>> L[2:4] = ['gray', 'purple']
>>> L
['yellow', 'blue', 'gray', 'purple', 'white']

Les éléments d'une liste peuvent être de différents types :

>>> L = [3, -200, 'hello']
>>> L
[3, -200, 'hello']
>>> L[1], L[2]
(-200, 'hello')

Pour un jeu de données numériques qui sont toutes du même type, il est souvent plus efficace d'utiliser un Array, un tableau, issu du module NumPy. Un tableau NumPy est un bloc mémoire contenant des éléments identiques. Avec les tableaux NumPy, les opérations peuvent être plus rapides car la disposition des éléments en mémoire est régulière et permet l'utilisation de routines en C à la place de boucles Python.

Python offre un large panel de fonctions pour modifier des listes, ou les interroger. Voici quelques exemples ; pour plus d'informations, veuillez consulter http://docs.python.org/tutorial/datastructures.html#more-on-lists.

Ajouter et supprimer des éléments :

>>> L = ['red', 'blue', 'green', 'black', 'white']
>>> L.append('pink')
>>> L
['red', 'blue', 'green', 'black', 'white', 'pink']
>>> L.pop() # removes and returns the last item
'pink'
>>> L
['red', 'blue', 'green', 'black', 'white']
>>> L.extend(['pink', 'purple']) # extend L, in-place
>>> L
['red', 'blue', 'green', 'black', 'white', 'pink', 'purple']
>>> L = L[:-2]
>>> L
['red', 'blue', 'green', 'black', 'white']

Inverser une liste :

>>> r = L[::-1]
>>> r
['white', 'black', 'green', 'blue', 'red']
>>> r2 = list(L)
>>> r2
['red', 'blue', 'green', 'black', 'white']
>>> r2.reverse() # in-place
>>> r2
['white', 'black', 'green', 'blue', 'red']

Concaténer et répéter une liste :

>>> r + L
['white', 'black', 'green', 'blue', 'red', 'red', 'blue', 'green', 'black', 'white']
>>> r * 2
['white', 'black', 'green', 'blue', 'red', 'white', 'black', 'green', 'blue', 'red']

Trier une liste :

>>> sorted(r) # new object
['black', 'blue', 'green', 'red', 'white']
>>> r
['white', 'black', 'green', 'blue', 'red']
>>> r.sort()  # in-place
>>> r
['black', 'blue', 'green', 'red', 'white']

Méthodes et POO

La notation r.method() (par exemple r.append(3) et L.pop()) constitue notre premier exemple de POO. Étant une liste, l'objet r possède la fonction method qui est appelée en utilisant la notation ".". Il n'y a aucun autre principe de la POO plus important que de comprendre la notation "." pour continuer ce tutoriel.

Méthodes de découvertes

Rappelez-vous : dans IPython, la complétion s'effectue avec la touche <Tab>.

In [28]: r.<TAB>
r.__add__ r.__iadd__ r.__setattr__
r.__class__ r.__imul__ r.__setitem__
r.__contains__ r.__init__ r.__setslice__
r.__delattr__ r.__iter__ r.__sizeof__
r.__delitem__ r.__le__ r.__str__
r.__delslice__ r.__len__ r.__subclasshook__
r.__doc__ r.__lt__ r.append
r.__eq__ r.__mul__ r.count
r.__format__ r.__ne__ r.extend
r.__ge__ r.__new__ r.index
r.__getattribute__ r.__reduce__ r.insert
r.__getitem__ r.__reduce_ex__ r.pop
r.__getslice__ r.__repr__ r.remove
r.__gt__ r.__reversed__ r.reverse
r.__hash__ r.__rmul__ r.sort

II-B-2 - Strings / Chaînes de caractères

Différentes syntaxes possibles (guillemets anglais simples ou doubles, éventuellement triplés) :

s = 'Hello, how are you?'
s = "Hi, what's up"
s = '''Hello,                 # tripling the quotes allows the
how are you'''         # the string to span more than one line
s = """Hi,
what's up?"""

In [1]: 'Hi, what's up?'
------------------------------------------------------------
File "<ipython console>", line 1
'Hi, what's up?'
^
SyntaxError: invalid syntax

Le caractère de saut de ligne est \n et celui de tabulation est \t.

Les chaînes de caractères (strings) constituent un jeu de données au même titre que les listes. Aussi peuvent-elles être indexées et échantillonnées, en utilisant les mêmes syntaxes et règles.

Indexation :

>>> a = "hello"
>>> a[0]
'h'
>>> a[1]
'e'
>>> a[-1]
'o'

Rappelez-vous que l'utilisation d'indice négatif permet de compter à partir de la fin.

Échantillonnage (slicing) :

>>> a = "hello, world!"
>>> a[3:6] # 3rd to 6th (excluded) elements: elements 3, 4, 5
'lo,'
>>> a[2:10:2] # Syntax: a[start:stop:step]
'lo o'
>>> a[::3] # every three characters, from beginning to end
'hl r!'

Les accents et les caractères spéciaux sont compris dans l'Unicode (voir http://docs.python.org/tutorial/introduction.html#unicode-strings).

Une chaîne de caractères (string) est un objet immuable et il n'est pas possible de modifier son contenu. On peut cependant créer un nouveau string à partir d'un objet existant.

In [53]: a = "hello, world!"
In [54]: a[2] = 'z'
---------------------------------------------------------------------------
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment

In [55]: a.replace('l', 'z', 1)
Out[55]: 'hezlo, world!'
In [56]: a.replace('l', 'z')
Out[56]: 'hezzo, worzd!'

Les strings possèdent de nombreuses méthodes fortes utiles comme a.replace tel que vu ci-dessus. Rappelez-vous que a. est une notion POO et utilisez la complétion par tabulation ou help(r) pour connaître les autres méthodes.

Python offre des possibilités évoluées pour manipuler les chaînes de caractères, la recherche de motifs (pattern) ou encore le formatage. Pour les plus passionnés, référez-vous à :

http://docs.python.org/library/stdtypes.html#string-methods

http://docs.python.org/library/string.html#new-string-formatting

Formatage de chaînes de caractères:

>>> 'An integer: %i; a float: %f; another string: %s' % (1, 0.1, 'string')
'An integer: 1; a float: 0.100000; another string: string'

>>> i = 102
>>> filename = 'processing_of_dataset_%d.txt' % i
>>> filename
'processing_of_dataset_102.txt'

II-B-3 - Dictionnaires

Un dictionnaire est basiquement un tableau associatif qui connecte ensemble des clés à des valeurs. C'est un conteneur non ordonné.

>>> tel = {'emmanuelle': 5752, 'sebastian': 5578}
>>> tel['francis'] = 5915
>>> tel
{'sebastian': 5578, 'francis': 5915, 'emmanuelle': 5752}
>>> tel['sebastian']
5578
>>> tel.keys()
['sebastian', 'francis', 'emmanuelle']
>>> tel.values()
[5578, 5915, 5752]
>>> 'francis' in tel
True

Il peut être utilisé pour stocker/rechercher de manière pratique des valeurs associées à un nom (un string pour une date, un nom...).

Consultez http://docs.python.org/tutorial/datastructures.html#dictionaries pour plus d'informations.

Un dictionnaire peut avoir des clés de différents types :

>>> d = {'a':1, 'b':2, 3:'hello'}
>>> d
{'a': 1, 3: 'hello', 'b': 2}

Quelques conteneurs supplémentaires.

TUPLES

Les tuples sont des listes immuables. Les éléments d'un tuple s'écrivent entre parenthèses, ou juste séparés par des virgules :

>>> t = 12345, 54321, 'hello!'
>>> t[0]
12345
>>> t
(12345, 54321, 'hello!')
>>> u = (0, 2)

SETS

Ensemble non ordonné, éléments uniques :

>>> s = set(('a', 'b', 'c', 'a'))
>>> s
set(['a', 'c', 'b'])
>>> s.difference(('a', 'b'))
set(['c'])

Les opérateurs d'assignation

La librairie de référence Python indique que : les déclarations d'affectations sont utilisées pour (re)nommer des valeurs et modifier les attributs ou les éléments d'un objet mutable.

En clair, cela fonctionne comme suit (simple assignation) :

1- L'expression à droite est analysée, puis l'objet correspondant est créé ou récupéré.

2- Le nom donné à gauche est affecté à l'objet de l'étape 1.

Veuillez noter que :

- un objet peut avoir plusieurs références rattachées ;

In [1]: a = [1, 2, 3]
In [2]: b = a
In [3]: a
Out[3]: [1, 2, 3]
In [4]: b
Out[4]: [1, 2, 3]
In [5]: a is b
Out[5]: True
In [6]: b[1] = 'hi!'
In [7]: a
Out[7]: [1, 'hi!', 3]

- pour modifier tout ou partie d'une liste, utilisez l'indexation ou l'échantillonnage :

In [1]: a = [1, 2, 3]
In [3]: a
Out[3]: [1, 2, 3]
In [4]: a = ['a', 'b', 'c'] # Creates another object.
In [5]: a
Out[5]: ['a', 'b', 'c']
In [6]: id(a)
Out[6]: 138641676
In [7]: a[:] = [1, 2, 3] # Modifies object in place.
In [8]: a
Out[8]: [1, 2, 3]
In [9]: id(a)
Out[9]: 138641676 # Same as in Out[6], yours will differ...

Le concept de clé ici est mutable vs immuable :

- Les objets mutables peuvent être modifiés à la volée.

- Les objets immuables ne peuvent être modifiés une fois créés.

Une très bonne explication, très détaillée, sur le cas ci-dessus peut être lu dans un article de David M. BEAZLEY

III - Contrôle de flux

Cela permet de contrôler l'ordre dans lequel le code va s'exécuter.

III-A - if/elif/else

>>> if 2**2 == 4:
... print 'Obvious!'
...
Obvious!

Les blocs de code sont délimités par l'indentation.

Saisissez les lignes suivantes dans votre interpréteur Python, et prenez garde à bien respecter l'indentation. Le shell IPython augmente automatiquement l'indentation après le caractère ":" ; pour diminuer l'indentation, pressez 4 fois la touche <backspace>. Appuyez sur <Entrée> deux fois pour quitter le bloc logique de code.

In [1]: a = 10
In [2]: if a == 1:
...: print(1)
...: elif a == 2:
...: print(2)
...: else:
...: print('A lot')
...:
A lot

L'indentation est obligatoire dans les scripts. Afin de vous exercer un peu, ressaisissez les lignes précédentes, avec la même indentation dans un script que vous nommerez condition.py et exécutez-le avec la commande run condition.py dans IPython.

III-B - for/range

Itération avec un index :

>>> for i in range(4):
... print(i)
0
1
2
3

Mais le plus souvent, il est plus pratique et plus lisible d'itérer sur les valeurs :

III-C - while/break/continue

On utilise le style de boucle while typique au C (problème de Mandelbrot) :

>>> z = 1 + 1j
>>> while abs(z) < 100:
... z = z**2 + 1
>>> z
(-134+352j)

Allons un peu plus loin

break permet de sortir d'une boucle for/while :

>>> z = 1 + 1j
>>> while abs(z) < 100:
... if z.imag == 0:
... break
... z = z**2 + 1

continue permet de sauter à l'itération suivante :

>>> a = [1, 0, 2, 4]
>>> for element in a:
... if element == 0:
... continue
... print 1. / element
1.0
0.5
0.25

expressions conditionnelles :

If <objet> :

Évalue à False :

- n'importe quel nombre égal à 0 (0, 0.0, 0+0j) ;

- un conteneur vide (liste, tuple, dictionnaire…) ;

- False, None.

Évalue à True :

- tout le reste.

a == b :

Test d'égalité, avec logique :

>>> 1 == 1.
True

a is b :

Test d'identité : il doit s'agir du même objet des deux côtés :

>>> 1 is 1.
False

>>> a = 1
>>> b = 1
>>> a is b
True

a in b :

Pour n'importe quel ensemble b : b contient a.

>>> b = [1, 2, 3]
>>> 2 in b
True
>>> 5 in b
False

Si b est un dictionnaire, ce test indique si a est une clé de b.

III-D - itérations avancées

III-D-1 - Itération de n'importe quelle séquence

Vous pouvez itérer n'importe quel type d'objet (string, liste, clés de dictionnaire, ligne de fichier…) :

>>> vowels = 'aeiouy'

>>> for i in 'powerful':
...     if i in vowels:
...         print(i),
o e u

>>> message = "Hello how are you?"
>>> message.split() # returns a list
['Hello', 'how', 'are', 'you?']
>>> for word in message.split():
...     print word
...
Hello
how
are
you?

Peu de langages (particulièrement les langages à but scientifique) permettent de boucler sur autre chose que des entiers ou des indices. Avec Python, il est possible de boucler sur ce qui vous intéresse sans vous soucier des indices qui ne vous intéressent pas. Cela permet souvent d'obtenir du code plus lisible.

Il n'est pas recommandé de modifier la séquence que vous êtes en train d'itérer.

III-D-2 - Garder une trace de l'index

Une des tâches classiques est d'itérer une séquence tout en gardant en mémoire le numéro de l'élément.

Vous pouvez utiliser une boucle while avec un compteur. Ou une boucle for :

>>> words = ('cool', 'powerful', 'readable')
>>> for i in range(0, len(words)):
...     print i, words[i]
0 cool
1 powerful
2 readable

Mais Python fournit le mot clé enumerate pour cela :

>>> for index, item in enumerate(words):
...     print index, item
0 cool
1 powerful
2 readable

III-D-3 - Boucler sur un dictionnaire

Utilisez iteritems :

>>> d = {'a': 1, 'b':1.2, 'c':1j}

>>> for key, val in d.iteritems():
...     print('Key: %s has value: %s' % (key, val))
Key: a has value: 1
Key: c has value: 1j
Key: b has value: 1.2

III-E - Liste en compréhension

>>> [i**2 for i in range(4)]
[0, 1, 4, 9]

III-F - Exercice

Calculez la décimale de pi en utilisant la formule de Wallis :

IV - Définir des fonctions

IV-A - Définition de fonctions

In [56]: def test():
....:     print('in test function')
....:
....:

In [57]: test()
in test function

Les blocs de code des fonctions doivent être indentés au même titre que les blocs de contrôle de flux.

IV-B - Effectuer un retour

Les fonctions peuvent potentiellement retourner des valeurs.

In [6]: def disk_area(radius):
...:     return 3.14 * radius * radius
...:

In [8]: disk_area(1.5)
Out[8]: 7.0649999999999995

À défaut, les fonctions retournent None.

Veuillez noter la syntaxe pour créer une fonction :

- le mot clé def ;

- ce mot clé est suivi par le nom de la fonction ;

- les arguments d'une fonction sont passés entre parenthèses, et suivi de « : » ;

- le corps de la fonction ;

- un return object pour potentiellement renvoyer des valeurs.

IV-C - Les paramètres

Paramètres obligatoires (arguments de positionnement) :

In [81]: def double_it(x):
....:     return x * 2
....:

In [82]: double_it(3)
Out[82]: 6

In [83]: double_it()
---------------------------------------------------------------------------
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: double_it() takes exactly 1 argument (0 given)

Paramètres optionnels (mots clés ou arguments nommés) :

In [84]: def double_it(x=2):
....:     return x * 2
....:

In [85]: double_it()
Out[85]: 4

In [86]: double_it(3)
Out[86]: 6

Les mots clés vous permettent de définir des valeurs par défaut.

Les valeurs par défaut sont à communiquer quand la fonction est définie, et non quand elle est appelée. Cela peut être d'autant plus problématique quand on utilise des types mutables (c'est-à-dire des dictionnaires ou des listes par exemple) et qu'on les modifie dans le corps de la fonction, puisque les modifications se propageront au-delà de l'appel de la fonction.

Utilisation d'un type immuable comme mot argument mot clé :

In [124]: bigx = 10

In [125]: def double_it(x=bigx):
.....:     return x * 2
.....:

In [126]: bigx = 1e9  # Now really big

In [128]: double_it()
Out[128]: 20

Utilisation d'un type mutable comme argument mot clé (et modification de ce dernier dans le corps de la fonction) :

In [2]: def add_to_dict(args={'a': 1, 'b': 2}):
...:     for i in args.keys():
...:         args[i] += 1
...:     print args
...:

In [3]: add_to_dict
Out[3]: <function __main__.add_to_dict>

In [4]: add_to_dict()
{'a': 2, 'b': 3}

In [5]: add_to_dict()
{'a': 3, 'b': 4}

In [6]: add_to_dict()
{'a': 4, 'b': 5}

Exemples plus complexes de la mise en œuvre de l'échantillonnage en Python :

In [98]: def slicer(seq, start=None, stop=None, step=None):
....:     """Implement basic python slicing."""
....:     return seq[start:stop:step]
....:

In [101]: rhyme = 'one fish, two fish, red fish, blue fish'.split()

In [102]: rhyme
Out[102]: ['one', 'fish,', 'two', 'fish,', 'red', 'fish,', 'blue', 'fish']

In [103]: slicer(rhyme)
Out[103]: ['one', 'fish,', 'two', 'fish,', 'red', 'fish,', 'blue', 'fish']

In [104]: slicer(rhyme, step=2)
Out[104]: ['one', 'two', 'red', 'blue']

In [105]: slicer(rhyme, 1, step=2)
Out[105]: ['fish,', 'fish,', 'fish,', 'fish']

In [106]: slicer(rhyme, start=1, stop=4, step=2)
Out[106]: ['fish,', 'fish,']

L'ordre des mots clés, en tant qu'arguments, importe peu :

In [107]: slicer(rhyme, step=2, start=1, stop=4)
Out[107]: ['fish,', 'fish,']

Mais il est considéré comme bonne pratique de garder le même ordre que dans la définition de la fonction.

Les mots clés comme arguments sont très pratiques afin de définir des fonctions avec un nombre variable d'arguments quand les valeurs par défaut doivent être utilisées dans la plupart des appels.

IV-D - Transmettre une valeur

Pouvons-nous modifier la valeur d'une variable à l'intérieur d'une fonction ? La plupart des langages (C, Java…) distinguent « passage par valeur » et « passage par référence ». En Python, une telle distinction est un peu superficielle, et il est subtil de savoir si vos variables vont être modifiées ou non. Heureusement, il existe des règles claires.

Les paramètres de fonctions sont liés à des objets, lesquels sont passés par valeur. Quand vous passez une variable à une fonction, Python communique la référence à l'objet auquel la variable se réfère, pas la variable elle-même. Si la valeur passée dans une fonction est immuable, la fonction ne modifie pas la variable de l'appelant. Si la variable est mutable, la fonction peut modifier la variable de l'appelant.

>>> def try_to_modify(x, y, z):
...     x = 23
...     y.append(42)
...     z = [99] # new reference
...     print(x)
...     print(y)
...     print(z)
...
>>> a = 77    # immutable variable
>>> b = [99]  # mutable variable
>>> c = [28]
>>> try_to_modify(a, b, c)
23
[99, 42]
[99]
>>> print(a)
77
>>> print(b)
[99, 42]
>>> print(c)
[28]

Les fonctions possèdent une table de variables locales appelées « espace de nom local ».

La variable x existe seulement à l'intérieur de la fonction try_to_modify().

IV-E - Les variables globales

Les variables déclarées en dehors de la fonction peuvent être référencées à l'intérieur de la fonction :

In [114]: x = 5

In [115]: def addx(y):
.....:     return x + y
.....:

In [116]: addx(10)
Out[116]: 15

Mais ces variables globales ne peuvent être modifiées à l'intérieur des fonctions, à moins de les déclarer comme globales (mot clé global) dans la fonction.

Ce qui suit ne fonctionne pas :

In [117]: def setx(y):
.....:     x = y
.....:     print('x is %d' % x)
.....:
.....:

In [118]: setx(10)
x is 10

In [120]: x
Out[120]: 5

Ceci, par contre, fonctionne :

In [121]: def setx(y):
.....:     global x
.....:     x = y
.....:     print('x is %d' % x)
.....:
.....:

In [122]: setx(10)
x is 10

In [123]: x
Out[123]: 10

IV-F - Nombres variables de paramètres

Forme des paramètres :

- *args : n'importe quel nombre d'arguments de position sous forme de tuple ;

- **kwargs : n'importe quel nombre d'arguments nommés (mots clés) sous forme de dictionnaire.

In [35]: def variable_args(*args, **kwargs):
....:     print 'args is', args
....:     print 'kwargs is', kwargs
....:

In [36]: variable_args('one', 'two', x=1, y=2, z=3)
args is ('one', 'two')
kwargs is {'y': 2, 'x': 1, 'z': 3}

IV-G - Les docstrings

Il s'agit de la documentation concernant ce que fait la fonction et ses paramètres. Voici la convention générale :

In [67]: def funcname(params):
....:     """Concise one-line sentence describing the function.
....:
....:     Extended summary which can contain multiple paragraphs.
....:     """
....:     # function body
....:     pass
....:

In [68]: funcname?
Type:           function
Base Class:     type 'function'>
String Form:    <function funcname at 0xeaa0f0>
Namespace:      Interactive
File:           <ipython console>
Definition:     funcname(params)
Docstring:
Concise one-line sentence describing the function.

Extended summary which can contain multiple paragraphs.

Guide sur les docstrings

Dans un but de standardisation, la page de Convention des docstrings documente la sémantique et les conventions concernant les docstrings Python.

Aussi, les modules Numpy et Scipy ont défini un standard précis pour la documentation des fonctions scientifiques, que vous devrez peut-être suivre pour vos propres fonctions avec une section Parameters, une section Examples…

IV-H - Les fonctions sont des objets

Les fonctions sont des objets multitâches, ce qui signifie qu'elles peuvent être :

- assignées à une variable ;

- un élément de liste (ou de tout autre collection) ;

- passées comme argument à une autre fonction.

In [38]: va = variable_args

In [39]: va('three', x=1, y=2)
args is ('three',)
kwargs is {'y': 2, 'x': 1}

IV-I - Les méthodes

Les méthodes sont des fonctions attachées à des objets. Vous avez déjà pu le constater dans nos exemples sur les listes, les dictionnaires, les chaînes de caractères…

IV-J - Exercice : Suite de Fibonacci

Écrire une fonction qui affiche les n premiers éléments de la suite de Fibonacci, définie par :

IV-K - Exercice : Tri rapide

Implémenter un algorithme de tri rapide, tel que défini sur wikipedia :

function quicksort(array)

var list less, greater if length(array) < 2

return array

select and remove a pivot value pivot from array for each x in array

if x < pivot + 1 then append x to less else append x to greater

return concatenate(quicksort(less), pivot, quicksort(greater))

V - Réutilisation de code : scripts et modules

Jusqu'à présent, nous avons saisi l'intégralité de nos instructions directement dans l'interpréteur. Pour des jeux d'instructions plus longs, il nous faut changer de direction et écrire le code dans des fichiers texte (avec un éditeur de texte), ce que nous appellerons « scripts » ou encore « modules ». Utilisez votre éditeur de texte préféré (peut-être propose-t-il aussi la coloration syntaxique Python), ou l'éditeur fourni avec la Scientific Python Suite que vous utilisez peut-être (par exemple, Scite avec Python(x,y)).

V-A - Les scripts

Pour commencer, écrivons un script, c'est un fichier avec une suite d'instructions qui sont exécutées chaque fois que le script est appelé.

Les instructions peuvent être par exemple copiées-collées depuis l'interpréteur (mais attention à respecter l'indentation).

L'extension des fichiers Python est .py. Écrivez ou copiez-collez les lignes suivantes dans un fichier test.py :

test.py

message = "Hello how are you?"
for word in message.split():
print word

Maintenant, exécutons ce script de manière interactive, à l'intérieur de l'interpréteur IPython. Ce doit être l'utilisation la plus classique dans la recherche scientifique.

Dans IPython, la syntaxe pour exécuter un script est %run script.py. Par exemple :

In [1]: %run test.py
Hello
how
are
you?
In [2]: message
Out[2]: 'Hello how are you?'

Le script a été exécuté. De plus, les variables définies dans le script (comme message) sont maintenant disponibles dans l'espace de nom de l'interpréteur.

Les autres interpréteurs offrent également la possibilité d'exécuter des scripts (par exemple, execfile dans l'interpréteur Python).

Il est également possible d'exécuter ce script comme un programme standalone, en lançant ce script à l'intérieur d'un terminal shell(console Linux/Mac, ou cmd pour Windows). Par exemple, si nous sommes dans le même dossier que notre script, nous pouvons exécuter les lignes suivantes dans une console :

$ python test.py
Hello
how
are
you?

Les scripts standalone peuvent également prendre des arguments. Ainsi, dans file.py :

file.py

import sys
print sys.argv

$ python file.py test arguments
['file.py', 'test', 'arguments']

N'essayez pas d'implémenter par vous-même un parsing. Utilisez les modules standards optparse, argparse ou docopt.

V-B - Importer des objets depuis des modules

In [1]: import os

In [2]: os
Out[2]: <module 'os' from '/usr/lib/python2.6/os.pyc'>

In [3]: os.listdir('.')
Out[3]:
['conf.py',
'basic_types.rst',
'control_flow.rst',
'functions.rst',
'python_language.rst',
'reusing.rst',
'file_io.rst',
'exceptions.rst',
'workflow.rst',
'index.rst']

Et aussi :

In [4]: from os import listdir

Import rapide :

In [5]: import numpy as np

from os import *

Ceci s'appelle le star import. Veuillez l'employer avec précaution.

- Cela peut rendre le code difficile à lire et à comprendre : d'où viennent précisément les divers symboles ?

- Cela peut rendre impossible à deviner une fonctionnalité dans un contexte.

- Cela peut restreindre les noms des variables que vous pouvez utiliser : os.name peut surcharger name et vice-versa.

- La création de nom peut poser problème entre modules.

- Cela peut rendre le code très difficile à déboguer.

Les modules sont également une bonne façon d'organiser son code de manière hiérarchique. Actuellement, tous les outils scientifiques utilisent des modules :

>>> import numpy as np # data arrays
>>> np.linspace(0, 10, 6)
array([  0.,   2.,   4.,   6.,   8.,  10.])
>>> import scipy # scientific computing

Avec Python(x,y), Ipython(x,y) exécutez les imports suivants au démarrage :

>>> import numpy
>>> import numpy as np
>>> from pylab import *
>>> import scipy

Et il ne sera plus nécessaire de réimporter ces modules.

V-C - Créer des modules

Si nous voulons créer des programmes plus importants et mieux structurés (en comparaison à de simples scripts), avec quelques définitions d'objets (variables, fonctions, classes) et que nous désirions les réutiliser de temps à autre, nous devons créer des modules.

Nous allons créer un module demo, dans un fichier demo.py :

demo.py

"A demo module."

def print_b():
"Prints b."
print 'b'

def print_a():
"Prints a."
print 'a'

c = 2
d = 2

Dans ce fichier, nous définissons deux fonctions print_a et print_b. Supposons que nous voulions appeler la fonction print_a depuis l'interpréteur. Nous pourrions exécuter le fichier comme un script, mais puisque nous voulons juste accéder à la fonction print_a, nous devons plutôt l'importer comme un module. La syntaxe est la suivante :

In [1]: import demo

In [2]: demo.print_a()
a

In [3]: demo.print_b()
b

Importer le module donne accès à ses objets, en utilisant la syntaxe module.objet. N'oubliez pas de mettre le nom du module avant le nom de l'objet, autrement Python ne reconnaîtra pas l'instruction.

V-C-1 - Introspection

In [4]: demo?
Type:               module
Base Class: <type 'module'>
String Form:        <module 'demo' from 'demo.py'>
Namespace:  Interactive
File:               /home/varoquau/Projects/Python_talks/scipy_2009_tutorial/source/demo.py
Docstring:
A demo module.

In [5]: who
demo

In [6]: whos
Variable   Type      Data/Info
------------------------------
demo       module    <module 'demo' from 'demo.py'>

In [7]: dir(demo)
Out[7]:
['__builtins__',
'__doc__',
'__file__',
'__name__',
'__package__',
'c',
'd',
'print_a',
'print_b']

In [8]: demo.
demo.__builtins__      demo.__init__          demo.__str__
demo.__class__         demo.__name__          demo.__subclasshook__
demo.__delattr__       demo.__new__           demo.c
demo.__dict__          demo.__package__       demo.d
demo.__doc__           demo.__reduce__        demo.print_a
demo.__file__          demo.__reduce_ex__     demo.print_b
demo.__format__        demo.__repr__          demo.py
demo.__getattribute__  demo.__setattr__       demo.pyc
demo.__hash__          demo.__sizeof__

V-C-2 - Import d'objet dans l'espace de nom principal

In [9]: from demo import print_a, print_b

In [10]: whos
Variable   Type        Data/Info
--------------------------------
demo       module      <module 'demo' from 'demo.py'>
print_a    function    <function print_a at 0xb7421534>
print_b    function    <function print_b at 0xb74214c4>

In [11]: print_a()
a

Module mis en cache

Les modules sont mis en cache. Si vous modifiez demo.py, puis le réimportez dans la session précédente, vous conserverez l'ancienne version.

Solution :

In [10]: reload(demo)

V-D - '__main__' et chargement de modules

Fichier demo2.py :

demo2.py

import sys

def print_a():
"Prints a."
print 'a'

print sys.argv

if __name__ == '__main__':
print_a()

import :

In [11]: import demo2
b

In [12]: import demo2

Lancement :

In [13]: %run demo2
b
a

V-E - Scripts ou modules : Comment organiser votre code ?

Règles de base

- Les jeux d'instructions qui sont appelés plusieurs fois devraient être écrits à l'intérieur de fonctions pour plus de réutilisabilité.

- Les fonctions (ou tout autre morceau de code) qui sont appelées depuis plusieurs modules différents devraient être écrites à l'intérieur de module, ainsi seul le module est importé dans les différents scripts (ne copiez-collez pas vos fonctions dans les différents scripts).

V-E-1 - Comment les modules sont trouvés et importés

Quand import mymodule est exécuté, le module mymodule est recherché dans une liste donnée de répertoire. Cette liste inclue une liste des dépendances par défaut (par exemple, /usr/lib/python sous Linux), ainsi qu'une liste de répertoires spécifiés dans la variable d'environnement PYTHONPATH.

La liste de ces dossiers recherchés par Python est fournie par la variable sys.path :

In [1]: import sys

In [2]: sys.path
Out[2]:
['',
'/home/varoquau/.local/bin',
'/usr/lib/python2.7',
'/home/varoquau/.local/lib/python2.7/site-packages',
'/usr/lib/python2.7/dist-packages',
'/usr/local/lib/python2.7/dist-packages',
...]

Les modules doivent être présents dans le chemin de recherche, aussi vous pouvez :

- écrire vos propres modules à l'intérieur de dossiers déjà définis dans le chemin de recherche (par exemple, $HOME/.local/lib/python2.7/dist-packages). Vous pouvez utiliser des liens symboliques (sous Linux) pour stocker votre code ailleurs ;

- modifier la variable d'environnement PYTHONPATH pour inclure les dossiers contenant les modules définis par l'utilisateur.

Sur Linux/Unix, ajoutez la ligne suivante à un fichier lu par le shell au démarrage (ex. /etc/profile, ou .profile).

export PYTHONPATH=$PYTHONPATH:/home/emma/user_defined_modules

Sous Windows, http://support.microsoft.com/kb/310519 explique comment configurer sa variable d'environnement.

- ou modifier le sys.path lui-même à l'intérieur d'un script Python.

import sys
new_path = '/home/emma/user_defined_modules'
if new_path not in sys.path:
sys.path.append(new_path)

Cette dernière méthode n'est pas très robuste ; de plus elle rend le code moins portable et vous devez redéfinir la variable chaque fois que vous désirez importer un module dans ce dossier.

Consultez http://docs.python.org/tutorial/modules.html pour plus d'informations.

V-F - Les packages

Un dossier qui contient plusieurs modules est appelé un paquetage (package). Un package peut contenir d'autres packages. Un fichier spécial, __init__.py (qui peut être vide) indique à Python que le dossier est un package Python, à partir duquel les modules peuvent être importés.

$ ls
cluster/        io/          README.txt@     stsci/
__config__.py@  LATEST.txt@  setup.py@       __svn_version__.py@
__config__.pyc  lib/         setup.pyc       __svn_version__.pyc
constants/      linalg/      setupscons.py@  THANKS.txt@
fftpack/        linsolve/    setupscons.pyc  TOCHANGE.txt@
__init__.py@    maxentropy/  signal/         version.py@
__init__.pyc    misc/        sparse/         version.pyc
INSTALL.txt@    ndimage/     spatial/        weave/
integrate/      odr/         special/
interpolate/    optimize/    stats/
$ cd ndimage
$ ls
doccer.py@   fourier.pyc   interpolation.py@  morphology.pyc   setup.pyc
doccer.pyc   info.py@      interpolation.pyc  _nd_image.so
setupscons.py@
filters.py@  info.pyc      measurements.py@   _ni_support.py@
setupscons.pyc
filters.pyc  __init__.py@  measurements.pyc   _ni_support.pyc  tests/
fourier.py@  __init__.pyc  morphology.py@     setup.py@

Avec IPython :

In [1]: import scipy

In [2]: scipy.__file__
Out[2]: '/usr/lib/python2.6/dist-packages/scipy/__init__.pyc'

In [3]: import scipy.version

In [4]: scipy.version.version
Out[4]: '0.7.0'

In [5]: import scipy.ndimage.morphology

In [6]: from scipy.ndimage import morphology

In [17]: morphology.binary_dilation?
Type:           function
Base Class:     <type 'function'>
String Form:    <function binary_dilation at 0x9bedd84>
Namespace:      Interactive
File:           /usr/lib/python2.6/dist-packages/scipy/ndimage/morphology.py
Definition:     morphology.binary_dilation(input, structure=None,
iterations=1, mask=None, output=None, border_value=0, origin=0,
brute_force=False)
Docstring:
Multi-dimensional binary dilation with the given structure.

An output array can optionally be provided. The origin parameter
controls the placement of the filter. If no structuring element is
provided an element is generated with a squared connectivity equal
to one. The dilation operation is repeated iterations times.  If
iterations is less than 1, the dilation is repeated until the
result does not change anymore.  If a mask is given, only those
elements with a true value at the corresponding mask element are
modified at each iteration.

V-G - Bonnes pratiques

- Utilisez des noms significatifs pour les objets.

- Utilisez l'indentation : aucune autre possibilité !

L'indentation est incontournable en Python ! Chaque bloc de commande se distingue du précédent par un niveau d'indentation supplémentaire.

Il doit obligatoirement en avoir après def f(): ou encore while:. À la fin de chacun de ces blocs, on doit décrémenter cette indentation d'un niveau (et le ré-augmenter si nouveau bloc).

Le respect strict de l'indentation est le prix à payer pour éviter d'avoir à utiliser des { et des ; qui délimitent les blocs logiques dans d'autres langages. Toute erreur d'indentations lève une exception :

------------------------------------------------------------
IndentationError: unexpected indent (test.py, line 2)

Cette histoire d'indentation peut paraître un peu déroutante au début. Cependant, la clarté de l'indentation et l'absence de caractères superflus permettent d'obtenir un code agréable à lire comparé à d'autres langages.

- Niveau d'indentation

À l'intérieur de votre éditeur de texte, vous pouvez choisir d'indenter avec n'importe quel nombre d'espaces (1,2,3,4…). Cependant, il est couramment admis d'indenter de quatre espaces. Vous pouvez ainsi paramétrer votre éditeur pour que la touche <Tab> corresponde à quatre espaces. Avec Python(x,y), l'éditeur est déjà préconfiguré.

- Mise en forme

Lignes trop longues : vous ne devriez pas écrire de très longues lignes qui dépassent les 80 caractères. Les lignes trop longues peuvent être coupées avec le caractère backslash \ immédiatement suivi d'un saut à la ligne :

>>> long_line = "Here is a very very long line \
... that we break in two parts."

Note du traducteur : la PEP8 autorise maintenant de monter jusqu'à 100 caractères.

Espaces : placez des espaces après les virgules, autour des opérateurs arithmétiques…

>>> a = 1 # yes
>>> a=1 # too cramped

Un certain nombre de règles d'écriture pour obtenir un code plus « agréable » (et plus) sont disponibles dans http://www.python.org/dev/peps/pep-0008.

Pour aller plus vite

Si vous désirez faire une première passe rapide sur l'écosystème de Scipy, vous pouvez d'ores et déjà passer au chapitre suivant :

http://scipy-lectures.github.io/intro/numpy/index.html#numpy

Le reste de ce chapitre n'est pas nécessaire pour comprendre Scipy. Cependant, n'oubliez pas de revenir et de finir ce chapitre plus tard.

VI - Entrées et sorties

Afin d'être exhaustif, voici quelques informations concernant les entrées et sorties avec Python. Puisque nous utiliserons les méthodes Numpy pour lire et écrire des fichiers, vous pouvez sauter ce chapitre si vous le désirez.

Nous écrivons ou lisons des chaînes de caractères vers/depuis des fichiers (n'importe quel type de donnée peut être converti en strings). Pour écrire dans un fichier :

>>> f = open('workfile', 'w') # opens the workfile file
>>> type(f)
<type 'file'>
>>> f.write('This is a test \nand another test')
>>> f.close()

Pour lire un fichier :

In [1]: f = open('workfile', 'r')

In [2]: s = f.read()

In [3]: print(s)
This is a test
and another test

In [4]: f.close()

Pour plus de détails : http://docs.python.org/tutorial/inputoutput.html

Itérer depuis un fichier :

In [6]: f = open('workfile', 'r')
In [7]: for line in f:
...: print line
...:
This is a test
and another test
In [8]: f.close()

VI-A - Modes de fichiers

- Lecture seule : 'r'

- Écriture seule : 'w'

- Note : permet de créer un nouveau fichier ou écrase un fichier existant.

- Écrire à la suite d'un fichier : 'a'

- Lire et écrire : 'r+'

- Travailler en mode binaire : 'b'

- Note : spécialement pour Windows.

VII - La librairie standard

Les documents de référence pour cette section sont :

- la documentation de la librairie standard Python : http://docs.python.org/library/index.html ;

- Python Essential Reference, de David BEAZLEY, professionnel chez Addison-Wesley.

VII-A - Module OS : Fonctionnalité de l'OS

« Un moyen simple d'accéder aux fonctionnalités spécifiques d'un système d'exploitation. »

VII-A-1 - Manipulation de fichiers et de dossiers

Dossier courant :

In [17]: os.getcwd()
Out[17]: '/Users/cburns/src/scipy2009/scipy_2009_tutorial/source'

Lister le contenu d'un dossier :

In [31]: os.listdir(os.curdir)
Out[31]:
['.index.rst.swo',
'.python_language.rst.swp',
'.view_array.py.swp',
'_static',
'_templates',
'basic_types.rst',
'conf.py',
'control_flow.rst',
'debugging.rst',
...

Créer un dossier :

In [32]: os.mkdir('junkdir')

In [33]: 'junkdir' in os.listdir(os.curdir)
Out[33]: True

Renommer un dossier :

In [36]: os.rename('junkdir', 'foodir')

In [37]: 'junkdir' in os.listdir(os.curdir)
Out[37]: False

In [38]: 'foodir' in os.listdir(os.curdir)
Out[38]: True

In [41]: os.rmdir('foodir')

In [42]: 'foodir' in os.listdir(os.curdir)
Out[42]: False

Supprimer un fichier :

In [44]: fp = open('junk.txt', 'w')

In [45]: fp.close()

In [46]: 'junk.txt' in os.listdir(os.curdir)
Out[46]: True

In [47]: os.remove('junk.txt')

In [48]: 'junk.txt' in os.listdir(os.curdir)
Out[48]: False

Manipulation de chemins d'accès :

os.path fournit les outils de manipulation de chemins d'accès :

In [70]: fp = open('junk.txt', 'w')

In [71]: fp.close()

In [72]: a = os.path.abspath('junk.txt')

In [73]: a
Out[73]: '/Users/cburns/src/scipy2009/scipy_2009_tutorial/source/junk.txt'

In [74]: os.path.split(a)
Out[74]: ('/Users/cburns/src/scipy2009/scipy_2009_tutorial/source',
'junk.txt')

In [78]: os.path.dirname(a)
Out[78]: '/Users/cburns/src/scipy2009/scipy_2009_tutorial/source'

In [79]: os.path.basename(a)
Out[79]: 'junk.txt'

In [80]: os.path.splitext(os.path.basename(a))
Out[80]: ('junk', '.txt')

In [84]: os.path.exists('junk.txt')
Out[84]: True

In [86]: os.path.isfile('junk.txt')
Out[86]: True

In [87]: os.path.isdir('junk.txt')
Out[87]: False

In [88]: os.path.expanduser('~/local')
Out[88]: '/Users/cburns/local'

In [92]: os.path.join(os.path.expanduser('~'), 'local', 'bin')
Out[92]: '/Users/cburns/local/bin'

Lancer des commandes externes :

In [8]: os.system('ls')
basic_types.rst   demo.py          functions.rst  python_language.rst  standard_library.rst
control_flow.rst  exceptions.rst   io.rst         python-logo.png
demo2.py          first_steps.rst  oop.rst        reusing_code.rst

Alternative àos.system

Le module sh est une alternative viable à os.system. Il fournit beaucoup de moyens permettant d'obtenir plus aisément les sorties, flux d'erreurs et codes de sortie issus des commandes externes.

In [20]: import sh
In [20]: com = sh.ls()

In [21]: print com
basic_types.rst   exceptions.rst   oop.rst              standard_library.rst
control_flow.rst  first_steps.rst  python_language.rst
demo2.py          functions.rst    python-logo.png
demo.py           io.rst           reusing_code.rst

In [22]: print com.exit_code
0
In [23]: type(com)
Out[23]: sh.RunningCommand

VII-A-2 - Analyser un dossier

os.path.walk permet d'obtenir la liste des fichiers contenus dans la structure d'un dossier donné.

In [10]: for dirpath, dirnames, filenames in os.walk(os.curdir):
....:     for fp in filenames:
....:         print os.path.abspath(fp)
....:
....:
/Users/cburns/src/scipy2009/scipy_2009_tutorial/source/.index.rst.swo
/Users/cburns/src/scipy2009/scipy_2009_tutorial/source/.view_array.py.swp
/Users/cburns/src/scipy2009/scipy_2009_tutorial/source/basic_types.rst
/Users/cburns/src/scipy2009/scipy_2009_tutorial/source/conf.py
/Users/cburns/src/scipy2009/scipy_2009_tutorial/source/control_flow.rst
...

Variables d'environnements :

In [9]: import os

In [11]: os.environ.keys()
Out[11]:
['_',
'FSLDIR',
'TERM_PROGRAM_VERSION',
'FSLREMOTECALL',
'USER',
'HOME',
'PATH',
'PS1',
'SHELL',
'EDITOR',
'WORKON_HOME',
'PYTHONPATH',
...

In [12]: os.environ['PYTHONPATH']
Out[12]: '.:/Users/cburns/src/utils:/Users/cburns/src/nitools:
/Users/cburns/local/lib/python2.5/site-packages/:
/usr/local/lib/python2.5/site-packages/:
/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5'

In [16]: os.getenv('PYTHONPATH')
Out[16]: '.:/Users/cburns/src/utils:/Users/cburns/src/nitools:
/Users/cburns/local/lib/python2.5/site-packages/:
/usr/local/lib/python2.5/site-packages/:
/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5'

VII-B - Module shutil : Opérations fichiers de haut niveau

shutil fournit des méthodes fort pratiques :

- shutil.rmtree : suppression de la structure complète d'un dossier, avec récursivité ;

- shutil.move : déplace un fichier ou un dossier à un autre emplacement, avec récursivité ;

- shutil.copy : permet de copier des fichiers ou des dossiers.

VII-C - Module glob : Recherche de pattern sur les fichiers

Le module glob fournit des outils de recherche de fichier, à base de pattern.

Par exemple, trouver tous les fichiers se finissant par .txt :

In [18]: import glob

In [19]: glob.glob('*.txt')
Out[19]: ['holy_grail.txt', 'junk.txt', 'newfile.txt']

VII-D - Module sys : Informations spécifiques du système

Informations spécifiques au système liées à l'interpréteur Python.

- Quelle version de Python êtes-vous en train d'utiliser et où est-il installé ?

In [117]: sys.platform
Out[117]: 'darwin'

In [118]: sys.version
Out[118]: '2.5.2 (r252:60911, Feb 22 2008, 07:57:53) \n
[GCC 4.0.1 (Apple Computer, Inc. build 5363)]'

In [119]: sys.prefix
Out[119]: '/Library/Frameworks/Python.framework/Versions/2.5'

- La liste des arguments passés à Python en ligne de commande :

In [100]: sys.argv
Out[100]: ['/Users/cburns/local/bin/ipython']

sys.path est une liste de strings qui indique les chemins utilisés afin de trouver des modules Python. Cette liste est initialisée depuis la variable PYTHONPATH.

In [121]: sys.path
Out[121]:
['',
'/Users/cburns/local/bin',
'/Users/cburns/local/lib/python2.5/site-packages/grin-1.1-py2.5.egg',
'/Users/cburns/local/lib/python2.5/site-packages/argparse-0.8.0-py2.5.egg',
'/Users/cburns/local/lib/python2.5/site-packages/urwid-0.9.7.1-py2.5.egg',
'/Users/cburns/local/lib/python2.5/site-packages/yolk-0.4.1-py2.5.egg',
'/Users/cburns/local/lib/python2.5/site-packages/virtualenv-1.2-py2.5.egg',
...

VII-E - Module pickle : persistance facile

Très pratique pour stocker arbitrairement des objets dans un fichier. Cependant, ce n'est pas le plus recommandé, ni le plus sûr.

In [1]: import pickle

In [2]: l = [1, None, 'Stan']

In [3]: pickle.dump(l, file('test.pkl', 'w'))

In [4]: pickle.load(file('test.pkl'))
Out[4]: [1, None, 'Stan']

VII-F - Exercice

Écrivez un programme pour rechercher votre PYTHONPATH pour le module site.py.

Solution

1. """Script to search the PYTHONPATH for the module site.py"""

3. import os

4. import sys

5. import glob

7. def find_module(module):

8. result = []

9. # Loop over the list of paths in sys.path

10. for subdir in sys.path:

11. # Join the subdir path with the module we're searching for

12. pth = os.path.join(subdir, module)

13. # Use glob to test if the pth is exists

14. res = glob.glob(pth)

15. # glob returns a list, if it is not empty, the pth exists

16. if len(res) > 0:

17. result.append(res)

18. return result

21. if __name__ == '__main__':

22. result = find_module('site.py')

23. print result

VIII - Les exceptions avec Python

Il est fort improbable que vous n'ayez jamais levé la moindre exception si vous avez saisi la totalité des commandes précédentes de ce tutoriel. Par exemple, vous avez dû en lever une si vous avez saisi une commande avec une faute de frappe.

Les exceptions sont levées pour différentes raisons, lesquelles surviennent à l'exécution du code Python. Dans votre propre code, vous vous devez d'intercepter les erreurs, ou de définir un traitement personnalisé.

Vous pouvez regarder les descriptions des exceptions déjà existantes quand vous en recherchez une.

VIII-A - Les exceptions

En Python, les exceptions sont levées par des erreurs :

In [1]: 1/0
---------------------------------------------------------------------------
ZeroDivisionError: integer division or modulo by zero

In [2]: 1 + 'e'
---------------------------------------------------------------------------
TypeError: unsupported operand type(s) for +: 'int' and 'str'

In [3]: d = {1:1, 2:2}

In [4]: d[3]
---------------------------------------------------------------------------
KeyError: 3

In [5]: l = [1, 2, 3]

In [6]: l[4]
---------------------------------------------------------------------------
IndexError: list index out of range

In [7]: l.foobar
---------------------------------------------------------------------------
AttributeError: 'list' object has no attribute 'foobar'

Comme vous pouvez le constater, il y a différents types d'exceptions pour différents types d'erreurs.

VIII-B - Intercepter les exceptions

VIII-B-1 - Try/except

In [10]: while True:
....:     try:
....:         x = int(raw_input('Please enter a number: '))
....:         break
....:     except ValueError:
....:         print('That was no valid number.  Try again...')
....:
Please enter a number: a
That was no valid number.  Try again...
Please enter a number: 1

In [9]: x
Out[9]: 1

VIII-B-2 - Try/finally

In [10]: try:
....:     x = int(raw_input('Please enter a number: '))
....: finally:
....:     print('Thank you for your input')
....:
....:
Please enter a number: a
Thank you for your input
---------------------------------------------------------------------------
ValueError: invalid literal for int() with base 10: 'a'

Important pour la gestion des ressources (par exemple, fermer un fichier).

VIII-B-3 - Réduire au silence une exception

In [11]: def print_sorted(collection):
....:     try:
....:         collection.sort()
....:     except AttributeError:
....:         pass
....:     print(collection)
....:
....:

In [12]: print_sorted([1, 3, 2])
[1, 2, 3]

In [13]: print_sorted(set((1, 3, 2)))
set([1, 2, 3])

In [14]: print_sorted('132')
132

VIII-B-4 - Lever des exceptions

Intercepter et propager une exception :

In [15]: def filter_name(name):
....:    try:
....:        name = name.encode('ascii')
....:    except UnicodeError, e:
....:        if name == 'Gaël':
....:            print('OK, Gaël')
....:        else:
....:            raise e
....:    return name
....:

In [16]: filter_name('Gaël')
OK, Gaël
Out[16]: 'Ga\xc3\xabl'

In [17]: filter_name('Stéfan')
---------------------------------------------------------------------------
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 2: ordinal not in range(128)

Comment propager des exceptions entre différentes sections de code ?

In [17]: def achilles_arrow(x):
....:    if abs(x - 1) < 1e-3:
....:        raise StopIteration
....:    x = 1 - (1-x)/2.
....:    return x
....:

In [18]: x = 0

In [19]: while True:
....:     try:
....:         x = achilles_arrow(x)
....:     except StopIteration:
....:         break
....:
....:

In [20]: x
Out[20]: 0.9990234375

Utilisez les exceptions pour notifier si certaines conditions sont remplies au cours de l'exécution du code (par exemple StopIteration) ou non (par exemple : custom error raising).

IX - Programmation orientée objet (POO)

Python supporte la programmation orientée objet (POO). Les buts principaux de la POO sont :

- organiser le code ;

- optimiser la ré-utilisabilité de code dans des contextes similaires.

Voici un petit exemple : nous avons créé une classe Student, laquelle est un objet contenant de multiples fonctions (ses méthodes) et de multiples variables (ses attributs), que nous pourrons utiliser :

>>> class Student(object):
...     def __init__(self, name):
...         self.name = name
...     def set_age(self, age):
...         self.age = age
...     def set_major(self, major):
...         self.major = major
...
>>> anna = Student('anna')
>>> anna.set_age(21)
>>> anna.set_major('physics')

Dans l'exemple précédent, la classe Student possède des méthodes __init__, set_age, et set_major. Ses attributs sont name, age, et major. Nous pouvons appeler ces méthodes et ces attributs avec la notation suivante : classinstance.methode ou bien encore classinstance.attribute. Le constructeur __init__ est une méthode spéciale que nous appelons avec : MyClass(paramètres d'initialisation si besoin).

Maintenant, supposons que nous désirions créer une nouvelle classe MasterStudent avec les mêmes méthodes et attributs que la précédente, mais avec un attribut complémentaire internship. Nous n'allons pas copier la classe précédente, mais en hériter :

>>> class MasterStudent(Student):
...     internship = 'mandatory, from March to June'
...
>>> james = MasterStudent('james')
>>> james.internship
'mandatory, from March to June'
>>> james.set_age(23)
>>> james.age
23

La classe MasterStudent hérite des attributs et des méthodes de la classe Student.

Grâce aux classes et à la programmation orientée objet, nous pouvons organiser notre code en différentes classes correspondant chacune aux divers objets dont nous pourrions avoir besoin (une classe Experience, une classe Image, une classe Flux…), chacune possédant ses propres méthodes et ses propres attributs. Puis nous pourrons utiliser l'héritage afin de créer des variations de ces classes en réutilisant ainsi leur code. Par exemple, depuis une classe Flux, nous pouvons créer des classes FluxDAlimentation, FluxDeTurbulence, FluxPotientiels…

X - Remerciements

Merci aux personnes suivantes pour leur aide pour cette traduction :

- Lana.Bauer

- tarball69

- f-leb