IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Analyse scientifique avec Python


précédentsommairesuivant

II. Initiation à Python

II-A. Introduction

II-A-1. Installation

Cette introduction repose essentiellement sur les outils suivants :

Ces logiciels peuvent être installés indépendamment, de préférence sous Linux (p.ex. Ubuntu ou votre distribution préférée), ou sous Windows ou MacOS. Il existe également des distributions « clés en main » :

II-A-2. Notions d'Unix

Les concepts suivants sont supposés connus :

  • ligne de commande : exécutables et options ;
  • arborescence : chemin relatif (./]...) et absolu (/...), navigation (cd) ;
  • gestion des fichiers (ls, rm, mv) et répertoires (mkdir) ;
  • gestion des exécutables : $PATH, chmod +x ;
  • gestion des processus : &, Control-c, Control-z + bg ;
  • variables d'environnement : export, .bashrc .

Liens :

II-A-3. L'invite de commande

Il existe principalement deux interpréteurs intéractifs de commandes Python :

 
Sélectionnez
1.
2.
3.
4.
5.
$ python
Python 2.7.6 (default, Mar 22 2014, 22:59:56)
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>
  • Control-d pour sortir ;
  • help(commande) pour obtenir l'aide d'une commande ;
  • A priori, pas d'historique des commandes ni de complétion automatique.

L'interpréteur de base permet également d'interpréter un « script », c.-à-d. un ensemble de commandes regroupées dans un fichier texte (généralement avec une extension .py) : python mon_script.py.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
$ ipython
Python 2.7.12 (default, Nov 19 2016, 06:48:10)
Type "copyright", "credits" or "license" for more information.

IPython 5.3.0 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]:
  • Control-d pour sortir ;
  • Tab pour la complétion automatique ;
  • Haut et Bas pour le rappel des commandes ;
  • Aide ipython : object? pour une aide sur un objet, object?? pour une aide plus complète (au niveau source) ;
  • Commandes magic (voir %magic) :

    • %run mon_script.py pour exécuter un script dans l'interpréteur,
    • %debug pour lancer le mode débogage interactif postmortem,
    • %cpaste pour coller et exécuter un code préformaté.

Liens :

II-B. Types de base

  • None (rien)
  • Chaînes de caractères : str

    • Entre (simples ou triples) apostrophes ' ou guillemets " : 'Calvin', "Calvin'n'Hobbes", '''Deux\nlignes''', """'Pourquoi?' demanda-t-il."""
    • Conversion : str(3.2)
  • Types numériques :

    • Booléens bool (vrai/faux) : True, False, bool(3)
    • Entiers int (pas de valeur limite explicite, correspond au moins au long du C) : -2, int(2.1), int("4")
    • Réels float (entre ±1.7e±308, correspond au double du C) : 2., 3.5e-6, float(3)
    • Complexes complex : 1+2j, 5.1j, complex(-3.14), complex('j')
 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
>>> 5 / 2       # !!! Division euclidienne par défaut dans Python 2.x !!!
2
>>> float(5)/2  # ou ajouter 'from __future__ import division' en début de script
2.5
>>> 6 // 2.5    # Division euclidienne explicite
2.0
>>> 6 % 2.5     # Reste de la division euclidienne
1.0
>>> (1 + 2j)**-0.5  # Puissance entière, réelle ou complexe
(0.5688644810057831-0.3515775842541429j)
  • Objets itérables :

    • Listes list : ['a', 3, [1, 2], 'a']
    • Listes immuables tuple : (2, 3.1, [1+0j], 'a') (selon les conditions d'utilisation, les parenthèses ne sont pas toujours nécessaires)
    • Listes à clés dict : {'a':1, 'b':[1, 2], 3:'c'}
    • Ensembles non ordonnés d'éléments uniques set : {1, 2, 3, 2}

       
      Sélectionnez
      1.
      2.
      3.
      4.
      5.
      6.
      7.
      8.
      >>> l = ['a', True] # Définition d'une liste
      >>> x, y = 1, 2.5   # Affectations multiples via tuples (les parenthèses ne sont pas nécessaires)
      >>> range(5)        # Liste de 5 entiers commençant par 0
      [0, 1, 2, 3, 4]
      >>> l + [x, y]      # Concaténation de listes
      ['a', True, 1, 2.5]
      >>> {2, 1, 3} | {1, 2, 'a'}  # Union d'ensembles
      {'a', 1, 2, 3}
      
  • type(obj) retourne le type de l'objet, isinstance(obj, type) teste le type de l'objet.
 
Sélectionnez
1.
2.
3.
4.
>>> type(l)
<type 'list'>
>>> isinstance(l, tuple)
False

Liens :

II-C. Structures de programmation

  • Les blocs sont définis par l'indentation (en général par pas de quatre espaces)(1).

    Évitez autant que possible les caractères de tabulation, source de confusion. Configurez votre éditeur de texte pour qu'il n'utilise que des espaces.

  • Une instruction par ligne en général (ou instructions séparées par ;).

  • Les commentaires commencent par #, et s'étendent jusqu'à la fin de la ligne.

  • Expression booléenne : une condition est une expression s'évaluant à True ou False :

    • False : test logique faux (p.ex. 3 == 4), valeur nulle, chaîne vide (''), liste vide ([]), etc.
    • True : test logique vrai (p.ex. 2 + 2 == 4), toute valeur ou objet non nul (et donc s'évaluant par défaut à True sauf exception)
    • Tests logiques : ==, !=, >, >=, etc.

      Ne pas confondre « = » (affectation d'une variable) et « == » (test logique d'égalité).

  • Opérateurs logiques : and, or, not

     
    Sélectionnez
    >>> (5 >= 6) or (not 3 > 4)
    True
  • Opérateur ternaire (PEP 308) : value if condition else altvalue, p.ex.
 
Sélectionnez
>>> y = x**0.5 if (x > 0) else 0  # Retourne sqrt(max(x, 0))
  • Expression conditionnelle : if condition: ... [elif condition2: ...] [else: ...], p.ex. :
 
Sélectionnez
1.
2.
3.
4.
5.
6.
if (i > 0):    # Condition principale
    print "positif"
elif (i < 0):  # Condition secondaire (si nécessaire)
    print "négatif"
else:          # Cas final (si nécessaire)
    print "nul"
  • Boucle for : for element in iterable , s'exécute sur chacun des éléments d'un objet itérable :
 
Sélectionnez
1.
2.
3.
4.
5.
>>> for val in ['un', (2, 3), 4]:  # Itération sur une liste de 3 éléments
...     print val
un
(2, 3)
4
  • continue : interrompt l'itération courante, et reprend la boucle à l'itération suivante,
  • break : interrompt complètement la boucle.

la logique des boucles Python est assez différente des langages C[++]/fortran, pour lesquels l'itération porte sur les indices plutôt que sur les éléments eux-mêmes.

  • Boucle while : while condition : se répète tant que la condition est vraie, ou jusqu'à une sortie explicite avec break.

Attention aux boucles infinies, dont la condition d'exécution reste invariablement vraie (typiquement un critère de convergence qui n'est jamais atteint). On peut toujours s'en protéger en testant en outre sur un nombre maximal (raisonnable) d'itérations :

 
Sélectionnez
1.
2.
3.
4.
niter = 0
while (error > 1e-6) and (niter < 100):
    error = ...   # A priori, error va décroître, et la boucle s'interrompre
    niter += 1    # Mais on n'est jamais assez prudent !

Exercices :

II-D. Les chaînes de caractères

II-D-1. Indexation

Les chaînes de caractères sont des objets itérables - c.-à-d. constitués d'éléments (ici les caractères) sur lesquels il est possible de « boucler » (p.ex. avec for) - et immuables - c.-à-d. dont les éléments individuels ne peuvent pas être modifiés intrinsèquement.

Comme en C[++], l'indexation en Python commence à 0 : le 1er élément d'une liste est l'élément n° 0, le 2e est le n° 1, etc. Les n éléments d'une liste sont donc indexés de 0 à n-1.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
>>> alpha = 'abcdefghijklmnopqrstuvwxyz'
>>> len(alpha)
26
>>> alpha[0]    # 1er élément (l'indexation commence à 0)
'a'
>>> alpha[-1]   # = alpha[26-1=25], dernier élément (-2: avant-dernier, etc.)
'z'

II-D-2. Sous-liste (slice)

Des portions d'une chaîne peuvent être extraites en utilisant des slice (« tranches »), de notation générique [start=0]:[stop=len][:step=1]. P.ex.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
>>> alpha[3:7]  # De l'élément n°3 (inclus) au n°7 (exclu), soit 7-3=4 éléments
'defg'
>>> alpha[:3]   # Du n°0 (défaut) au n°3 (exclu), soit 3 éléments
'abc'
>>> alpha[-3:]  # Du n°26-3=23 (inclus) au dernier inclus (défaut)
'xyz'
>>> alpha[8:23:3] # Du n°8 (inclus) au n°23 (exclu), tous les 3 éléments
'iloru'
>>> alpha[::5]    # Du 1er au dernier élément (défauts), tous les 5 éléments
'afkpuz'

II-D-3. Méthodes

Les chaînes de caractères disposent de nombreuses fonctionnalités - appelées « méthodes » en POOProgrammation Orientée Objet - facilitant leur manipulation :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
>>> enfant, peluche = "Calvin", 'Hobbes'      # Affectations mutiples
>>> titre = enfant + ' et ' + peluche; titre  # +: Concaténation de chaînes
'Calvin et Hobbes'
>>> titre.replace('et', '&')  # Remplacement de sous-chaînes (→ nouvelle chaîne)
'Calvin & Hobbes'
>>> titre                     # titre est immuable et reste inchangé
'Calvin et Hobbes'
>>> ' & '.join(titre.split(' et ')) # Découpage (split) et jonction (join)
'Calvin & Hobbes'
>>> 'Hobbes' in titre               # in: Test d'inclusion
True
>>> titre.find("Hobbes")            # str.find: Recherche de sous-chaîne
10
>>> titre.center(30, '-')
'-------Calvin et Hobbes-------'
>>> dir(str)                        # Liste toutes les méthodes des chaînes

II-D-4. Formatage

Le système de formatage permet un contrôle précis de la conversion de variables en chaînes de caractères. Il s'appuie essentiellement sur la méthode str.format() :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
>>> "{0} a {1} ans".format('Calvin', 6)  # args
'Calvin a 6 ans'
>>> "{} a {} ans".format('Calvin', 6)    # Raccourci
'Calvin a 6 ans'
>>> "{nom} a {age} ans".format(nom='Calvin', age=6)  # kwargs
'Calvin a 6 ans'
>>> pi = 3.1415926535897931
>>> "{x:f}  {x:.2f}  {y:f}  {y:g}".format(x=pi, y=pi*1e9)  # Options de formatage
'3.141593  3.14  3141592653.589793  3.14159e+09'

print() affiche à l'écran (plus spécifiquement la sortie standard) la conversion d'une variable en chaîne de caractères :

 
Sélectionnez
1.
2.
3.
4.
5.
>>> print "Calvin and Hobbes\nScientific progress goes 'boink'"
Calvin and Hobbes
Scientific progress goes 'boink'
>>> print "{0:2d} fois {1:2d} font {2}".format(3, 4, 3*4) # Formatage et affichage
 3 fois  4 font 12

Exercice :

II-E. Objets itérables

Les chaînes de caractères, listes, tuples et dictionnaires sont les objets itérables de base en Python. Les listes et dictionnaires sont modifiables (« mutables ») - leurs éléments constitutifs peuvent être changés à la volée - tandis que chaînes de caractères et les tuples sont immuables.

  • Accès indexé : conforme à celui des chaînes de caractères

     
    Sélectionnez
    1.
    2.
    3.
    4.
    5.
    6.
    7.
    8.
    9.
    10.
    11.
    12.
    13.
    14.
    15.
    16.
    >>> l = range(1, 10, 2); l  # De 1 (inclus) à 10 (exclu) par pas de 2
    [1, 3, 5, 7, 9]
    >>> len(l)          # Nb d'éléments dans la liste (i varie de 0 à 4)
    5
    >>> l[0], l[-2]     # 1er et avant-dernier élément (l'indexation commence à 0)
    (1, 7)
    >>> l[5]            # Erreur: indice hors-bornes
    IndexError: list index out of range
    >>> d = dict(a=1, b=2) # Création du dictionnaire {'a':1, 'b':2}
    >>> d['a']          # Accès à une entrée via sa clé
    1
    >>> d['c']          # Erreur: clé inexistante !
    KeyError: 'c'
    >>> d['c'] = 3; d   # Ajout d'une clé et sa valeur
    {'a': 1, 'c': 3, 'b': 2}
    >>> # Noter qu'un dictionnaire N'est PAS ordonné !
    
  • Sous-listes (slices) :

     
    Sélectionnez
    1.
    2.
    3.
    4.
    5.
    6.
    >>> l[1:-1]        # Du 2e ('1') *inclus* au dernier ('-1') *exclu*
    [3, 5, 7]
    >>> l[1:-1:2]      # Idem, tous les 2 éléments
    [3, 7]
    >>> l[::2]         # Tous les 2 éléments (*start=0* et *stop=len* par défaut)
    [1, 5, 9]
    
  • Modification d'éléments d'une liste (chaînes et tuples sont immuables) :

     
    Sélectionnez
    1.
    2.
    3.
    4.
    5.
    6.
    7.
    8.
    9.
    10.
    11.
    12.
    13.
    14.
    15.
    >>> l[0] = 'a'; l            # Remplacement du 1er élément
    ['a', 3, 5, 7, 9]
    >>> l[1::2] = ['x', 'y']; l  # Remplacement d'éléments par *slices*
    ['a', 'x', 5, 'y', 9]
    >>> l + [1, 2]; l            # Concaténation (l reste inchangé)
    ['a', 'x', 5, 'y', 9, 1, 2]
    ['a', 'x', 5, 'y', 9]
    >>> l += [1, 2]; l           # Concaténation sur place (l est modifié)
    ['a', 'x', 5, 'y', 9, 1, 2]
    >>> l.append('z'); l         # Ajout d'un élément en fin de liste
    ['a', 'x', 5, 'y', 9, 1, 2, 'z']
    >>> l.extend([-1, -2]); l    # Extension par une liste
    ['a', 'x', 5, 'y', 9, 1, 2, 'z', -1, -2]
    >>> del l[-6:]; l            # Efface les 6 derniers éléments de la liste
    ['a', 'x', 5, 'y']
    

    Attention à la modification des objets mutables :

     
    Sélectionnez
    1.
    2.
    3.
    4.
    5.
    6.
    7.
    8.
    9.
    10.
    11.
    12.
    13.
    14.
    15.
    16.
    >>> l = range(3)  # l pointe vers la liste [0, 1, 2]
    >>> m = l; m      # m est un *alias* de la liste l: c'est le même objet
    [0, 1, 2]
    >>> id(l); id(m); m is l
    171573452         # id({obj}) retourne le n° d'identification en mémoire
    171573452         # m et l ont le même id:
    True              # ils correspondent donc bien au même objet en mémoire
    >>> l[0] = 'a'; m # puisque l a été modifiée, il en est de même de m
    ['a', 1, 2]
    >>> m = l[:]      # copie de tous les éléments de l dans une *nouvelle* liste m (clonage)
    >>> id(l); id(m); m is l
    171573452
    171161228         # m a un id différent de l: il s'agit de 2 objets distincts
    False             # (contenant éventuellement la même chose !)
    >>> del l[-1]; m  # les éléments de m n'ont pas été modifiés
    ['a', 1, 2]
    
  • Liste en compréhension : elle permet la construction d'une liste à la volée
 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
>>> [ i**2 for i in range(5) ] # Carré de tous les éléments de [0, ..., 4]
[0, 1, 4, 9, 16]
>>> [ 2*i for i in range(10) if (i%3 != 0) ]  # Compréhension conditionnelle
[2, 4, 8, 10, 14, 16]
>>> [ 10*i+j for i in range(3) for j in range(4) ]     # Double compréhension
[0, 1, 2, 3, 10, 11, 12, 13, 20, 21, 22, 23]
>>> [ [ 10*i+j for i in range(3) ] for j in range(4) ] # Compréhensions imbriquées
[[0, 10, 20], [1, 11, 21], [2, 12, 22], [3, 13, 23]]
>>> { i: i**2 for i in range(1, 5) }  # Dictionnaire en compréhension
{1: 1, 2: 4, 3: 9, 4: 16}
  • Utilitaires sur les itérables :
 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
>>> humans = ['Calvin', 'Wallace', 'Boule']
>>> for i in range(len(humans)):  # Boucle sur les indices de humans
...     print i, humans[i]        # Accès explicite, pas pythonique :-(
0 Calvin
1 Wallace
2 Boule
>>> for i, name in enumerate(humans):  # Boucle sur (indice, valeur) de humans
...     print i, name                  # Pythonique :-D
0 Calvin
1 Wallace
2 Boule
>>> animals = ['Hobbes', 'Gromit', 'Bill']
>>> for boy, dog in zip(humans, animals):  # Boucle simultanée sur 2 listes (ou +)
...     print boy, 'et', dog
Calvin et Hobbes
Wallace et Gromit
Boule et Bill
>>> sorted(zip(humans, animals))  # Tri, ici sur le 1er élément de chaque tuple de la liste
[('Boule', 'Bill'), ('Calvin', 'Hobbes'), ('Wallace', 'Gromit')]

Exercices :

II-F. Fonctions

Une fonction est un regroupement d'instructions impératives - assignations, branchements, boucles, etc. - s'appliquant sur des arguments d'entrée. C'est le concept central de la programmation impérative.

def permet de définir une fonction :

 
Sélectionnez
def fonction(arg1, arg2, ..., option1=valeur1, option2=valeur2, ...):

Les « args » sont des arguments nécessaires (c.-à-d. obligatoires), tandis que les « kwargs » - arguments de type option=valeur - sont optionnels, puisqu'ils possèdent une valeur par défaut. Si la fonction doit retourner une valeur, celle-ci est spécifiée par le mot-clé return.

Exemples :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
def temp_f2c(tf):
    """
    Convertit une température en d° Fahrenheit `tf` en d° Celsius.

    Exemple:
    >>> temp_f2c(104)
    40.0
    """

    tc = (tf - 32.)/1.8       # Fahrenheit ? Celsius

    return tc

Dans la définition d'une fonction, la première chaîne de caractères (appelé docstring) servira de documentation pour la fonction, accessible de l'interpréteur via p.ex. help(temp_f2c), ou temp_f2c? sous ipython. Elle se doit d'être tout à la fois pertinente, concise et complète. Elle peut également inclure des exemples d'utilisation (doctests, voir Développement piloté par les testsDéveloppement piloté par les tests).

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
def mean_power(alist, power=1):
    """
    Retourne la racine `power` de la moyenne des éléments de `alist` à
    la puissance `power`:

    .. math:: \mu = (\frac{1}{N}\sum_{i=0}^{N-1} x_i^p)^{1/p}

    `power=1` correspond à la moyenne arithmétique, `power=2` au *Root
    Mean Squared*, etc.

    Exemples:
    >>> mean_power([1, 2, 3])
    2.0
    >>> mean_power([1, 2, 3], power=2)
    2.160246899469287
    """

    s = 0.                  # Initialisation de la variable *s* comme *float*
    for val in alist:       # Boucle sur les éléments de *alist*
        s += val ** power   # *s* est augmenté de *val* puissance *power*
    # *mean* = (somme valeurs / nb valeurs)**(1/power)
    mean = (s / len(alist)) ** (1 / power)  # ATTENTION aux divisions euclidiennes !

    return mean

Il faut noter plusieurs choses importantes :

  • Python est un langage à typage dynamique, p.ex., le type des arguments d'une fonction n'est pas fixé a priori. Dans l'exemple précédent, alist peut être une list, un tuple ou tout autre itérable contenant des éléments pour lesquels les opérations effectuées - somme, exponentiation, division par un entier - ont été préalablement définies (p.ex. des entiers, des complexes, des matrices, etc.) ;
  • le typage est fort, c.-à-d. que le type d'une variable ne peut pas changer à la volée. Ainsi, "abra" + "cadabra" a un sens (concaténation de chaînes), mais pas 3 + "cochons" (entier + chaîne) ;
  • la définition d'une fonction se fait dans un « espace parallèle » où les variables ont une portée (scope) locale(2). Ainsi, la variable s définie dans la fonction mean_power n'interfère pas avec le « monde extérieur » ; inversement, la définition de mean_power ne connaît a priori rien d'autre que les variables explicitement définies dans la liste des arguments ou localement.

Exercice :

II-G. Bibliothèques et scripts

II-G-1. Bibliothèques externes

Une bibliothèque (ou module) est un code fournissant des fonctionnalités supplémentaires - p.ex. des fonctions prédéfinies - à Python. Ainsi, le module math définit les fonctions et constantes mathématiques usuelles (sqrt(), pi, etc.).

Une bibliothèque est « importée » avec la commande import module. Les fonctionnalités supplémentaires sont alors accessibles dans l'espace de noms module via module.fonction :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
>>> sqrt(2)                   # sqrt n'est pas une fonction standard de python
NameError: name 'sqrt' is not defined
>>> import math               # Importe tout le module 'math'
>>> dir(math)                 # Liste les fonctionnalités de 'math'
['__doc__', '__name__', '__package__', 'acos', 'acosh', 'asin',
'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh',
'degrees', 'e', 'exp', 'fabs', 'factorial', 'floor', 'fmod', 'frexp',
'fsum', 'hypot', 'isinf', 'isnan', 'ldexp', 'log', 'log10', 'log1p',
'modf', 'pi', 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh',
'trunc']
>>> math.sqrt(math.pi)        # Les fonctionnalités sont disponibles sous 'math'
1.7724538509055159
>>> import math as M          # Importe 'math' dans l'espace 'M'
>>> M.sqrt(M.pi)
1.7724538509055159
>>> from math import sqrt, pi # Importe uniquement 'sqrt' et 'pi' dans l'espace courant
>>> sqrt(pi)
1.7724538509055159

Il est possible d'importer toutes les fonctionnalités d'une bibliothèque dans l'espace de noms courant :

 
Sélectionnez
1.
2.
3.
>>> from math import *    # Argh ! Pas pythonique :-(
>>> sqrt(pi)
1.7724538509055159

Cette pratique est cependant fortement déconseillée du fait des confusions dans les espaces de noms qu'elle peut entraîner :

 
Sélectionnez
>>> from cmath import *
>>> sqrt(-1)   # Quel sqrt: le réel ou le complexe ?

Nous verrons par la suite quelques exemples de modules de la Bibliothèque standardBibliothèque standard, ainsi que des Bibliothèques numériques de baseBibliothèques numériques de base orientées analyse numérique.

Exercice :

Flocon de Koch (programmation récursive) ★★★Flocon de Koch (programmation récursive) ★★★

II-G-2. Bibliothèques personnelles et scripts

Vous pouvez définir vos propres bibliothèques en regroupant les fonctionnalités au sein d'un même fichier monfichier.py.

  • Si ce fichier est importé (p.ex. import monfichier), il agira comme une bibliothèque.
  • Si ce fichier est exécuté - p.ex. python ./monfichier.py - il agira comme un script.

Toutes les instructions d'un module qui ne sont pas encapsulées dans le __main__ (voir plus bas) sont interprétées et exécutées lors de l'import du module. Elles doivent donc en général se limiter à la définition de variables, de fonctions et de classes (en particulier, éviter les affichages ou les calculs longs).

Un code Python peut donc être :

  • un module - s'il n'inclut que des définitions mais pas d'instruction exécutable en dehors d'un éventuel __main__ ;
  • un exécutable - s'il inclut un __main__ ou des instructions exécutables ;
  • ou les deux à la fois.

Exemple 

Le code mean_power.pyMean power (fonction, argparse) peut être importé comme une bibliothèque (p.ex. import mean_power) dans un autre code Python, ou bien être exécuté depuis la ligne de commande (p.ex. python mean_power.py), auquel cas la partie __main__ sera exécutée.

  • #! (Hash-bang) : la première ligne d'un script défini l'interpréteur à utiliser(3) :

     
    Sélectionnez
    #!/usr/bin/env python
  • Un fichier incluant des caractères non ASCII (p.ex. caractères accentués, ou symboles UTF tels que ±) doit définir le système d'encodage, généralement utf-8 :

     
    Sélectionnez
    # -*- coding: utf-8 -*-

    Notez que les noms de variables, fonctions, etc. doivent être purement ASCII (a-zA-Z0-9_). De manière générale, favorisez la langue anglaise (variables, commentaires, affichages).

  • """doc""" : la chaîne de documentation de la bibliothèque (docstring, PEP 257), qui sera utilisée comme aide en ligne du module (help(mean_power)), doit être la 1re instruction du script.

  • from __future__ import division : permet de ne pas considérer les divisions entre entiers comme euclidiennes par défaut.

  • if __name__ == '__main__' : permet de séparer le __main__ (c.-à-d. le corps du programme, à exécuter lors d'une utilisation en script) des définitions de fonctions et classes, permettant une utilisation en module.

II-H. Exceptions

Lorsqu'il rencontre une erreur dans l'exécution d'une instruction, l'interpréteur Python génère (raise) une erreur (Exception), de nature différente selon la nature de l'erreur : KeyError, ValueError, AttributeError, NameError, TypeError, IOError, NotImplementedError, etc. La levée d'une erreur n'est cependant pas nécessairement fatale, puisque Python dispose d'un mécanisme de gestion des erreurs.

Il est d'usage en Python d'utiliser la philosophie EAFPEasier to Ask for Forgiveness than Permission[4] : plutôt que de tester explicitement toutes les conditions de validité d'une instruction, on « tente sa chance » d'abord, quitte à gérer les erreurs a posteriori. Cette gestion des Exception se fait par la construction try ... except.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
def lireEntier():
    while True:
        chaine = raw_input('Entrez un entier: ') # Lecture du clavier
        try:
            # La conversion en type entier génère `ValueError` si nécessaire
            return int(chaine)
        except ValueError:             # Gestion de l'exception ValueError
            print "'{}' n'est pas un entier".format(chaine)
 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
>>> lireEntier()
Entrez un entier: toto
'toto' n'est pas un entier
Entrez un entier: 3,4
'3,4' n'est pas un entier
Entrez un entier: 4
4

Dans l'élaboration d'un programme, gérez explicitement les erreurs que vous auriez pu tester a priori et pour lesquels il existe une solution de repli, et laissez passer les autres (ce qui provoquera éventuellement l'interruption du programme).

Évitez à tout prix les except nus, c.-à-d. ne spécifiant pas la ou les exceptions à gérer, car ils intercepteraient alors toutes les exceptions, y compris celles que vous n'aviez pas prévues ! Trouvez l'erreur dans le code suivant :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
y = 2
try:
   x = z                 # Copie y dans x
   print "Tout va bien"
except:
   print "Rien ne va plus"

Vos procédures doivent également générer des exceptions (documentées) - avec l'instruction raise Exception - si elles ne peuvent conclure leur action, à charge pour la procédure appelante de les gérer si besoin :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
def diff_sqr(x, y):
    """
    Return x**2 - y**2 for x >= y, raise ValueError otherwise.

    Exemples:
    >>> diff_sqr(5, 3)
    16
    >>> diff_sqr(3, 5)
    Traceback (most recent call last):
    ...
    ValueError: x=3 < y=5
    """

    if x < y:
        raise ValueError("x={} < y={}".format(x, y))

    return x**2 - y**2

Avant de se lancer dans un calcul long et complexe, on peut vouloir tester la validité de certaines hypothèses fondamentales, soit par une structure if ... raise, ou plus facilement à l'aide d'assert (qui, si l'hypothèse n'est pas vérifiée, génère une AssertionError) :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
def diff_sqr(x, y):
    """
    Returns x**2 - y**2 for x >= y, AssertionError otherwise.
    """

    assert x >= y, "x={} < y={}".format(x, y) # Test et msg d'erreur
    return x**2 - y**2

La règle générale à retenir concernant la gestion des erreurs :

Fail early, fail often, fail better !

Exercice :

Jeu du plus ou moins (exceptions) ★Jeu du plus ou moins (exceptions) ★

II-I. Classes

Un objet est une entité de programmation, disposant de ses propres états et fonctionnalités. C'est le concept central de la Programmation Orientée Objet.

Au concept d'objet sont liées les notions de :

  • Classe : il s'agit d'un modèle d'objet, dans lequel sont définis ses propriétés usuelles. P.ex. la classe Forme peut représenter une forme plane caractérisée par sa couleur, et disposant de fonctionnalités propres, p.ex. change_couleur() ;
  • Instanciation : c'est le fait générer un objet concret (une instance) à partir d'un modèle (une classe). P.ex. rosie = Forme('rose') crée une instance rosie à partir de la classe Forme et d'une couleur (chaîne de caractères 'rose') ;
  • Attributs : variables internes décrivant l'état de l'objet. P.ex., rosie.couleur donne la couleur de la Forme rosie ;
  • Méthodes : fonctions internes, s'appliquant en premier lieu sur l'objet lui-même (self), décrivant les capacités de l'objet. P.ex. rosie.change_couleur('bleu') change la couleur de la Forme rosie.

    Toutes les méthodes d'une classe doivent au moins prendre self - représentant l'objet lui-même - comme premier argument.

  • Surcharge d'opérateurs : cela permet de redéfinir les opérateurs et fonctions usuels (+, abs(), str(), etc.), pour simplifier l'écriture d'opérations sur les objets. Ainsi, on peut redéfinir les opérateurs de comparaison (<, >=, etc.) dans la classe Forme pour que les opérations du genre forme1 < forme2 aient un sens (p.ex. en comparant les aires).
    Liste des méthodes standard et des surcharges d'opérateur ;

  • Héritage de classe : il s'agit de définir une classe à partir d'une (ou plusieurs) classe(s) parente(s). La nouvelle classe hérite des attributs et méthodes de sa (ses) parente(s), que l'on peut alors modifier ou compléter. P.ex. la classe Rectangle hérite de la classe Forme (elle partage la notion de couleur et d'aire), et lui ajoute des méthodes propres à la notion de rectangle (p.ex. formule explicite de l'aire, étirement).

Toutes les classes doivent au moins hériter de la classe principale object.

Exemple de définition de classe

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
class Forme(object):  # *object* est la classe dont dérivent toutes les autres

    """Une forme plane, avec éventuellement une couleur."""

    def __init__(self, couleur=None):
        """Initialisation d'une Forme, sans couleur par défaut."""

        if couleur is None:
            self.couleur = 'indéfinie'
        else:
            self.couleur = couleur

    def __str__(self):
        """
        Surcharge de la fonction `str()`: l'affichage *informel* de
        l'objet dans l'interpréteur, p.ex. `print a` sera résolu comme
        `a.__str__()`

        Retourne une chaîne de caractères.
        """

        return "Forme encore indéfinie de couleur {}".format(self.couleur)

    def change_couleur(self, newcolor):
        """Change la couleur de la Forme."""

        self.couleur = newcolor

    def aire(self):
        """
        Renvoi l'aire de la Forme.

        L'aire ne peut pas être calculée dans le cas où la forme n'est
        pas encore spécifiée: c'est ce que l'on appelle une méthode
        'abstraite', qui pourra être précisée dans les classes filles.
        """

        raise NotImplementedError(
            "Impossible de calculer l'aire d'une forme indéfinie.")

    def __cmp__(self, other):
        """
        Comparaison de deux Formes sur la base de leur aire.

        Surcharge des opérateurs de comparaison de type `{self} <
        {other}`: la comparaison sera résolue comme
        `self.__cmp__(other)` et le résultat sera correctement
        interprété.

        .. WARNING:: cette construction n'est plus supportée en Python3.
        """

        return cmp(self.aire(), other.aire())  # Opérateur de comparaison

Exemple d'héritage de classe

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
class Rectangle(Forme):

    """
    Un Rectangle est une Forme particulière.

    La classe fille hérite des attributs et méthodes de la
    classe mère, mais peut les surcharger (i.e. en changer la
    définition), ou en ajouter de nouveaux :

    - les méthodes `Rectangle.change_couleur()` et
      `Rectangle.__cmp__()` dérivent directement de
      `Forme.change_couleur()` et `Forme.__cmp__()`;
    - `Rectangle.__str__()` surcharge `Forme.__str__()`;
    - `Rectangle.aire()` définit la méthode jusqu'alors abstraite
      `Forme.aire()`;
    - `Rectangle.allonger()` est une nouvelle méthode propre à
      `Rectangle`.
    """

    def __init__(self, longueur, largeur, couleur=None):
        """
        Initialisation d'un Rectangle longueur × largeur, sans couleur par
        défaut.
        """

        # Initialisation de la classe parente (nécessaire pour assurer
        # l'héritage)
        Forme.__init__(self, couleur)

        # Attributs propres à la classe Rectangle
        self.longueur = longueur
        self.largeur = largeur

    def __str__(self):
        """Surcharge de `Forme.__str__()`."""

        return "Rectangle {}x{}, de couleur {}".format(
            self.longueur, self.largeur, self.couleur)

    def aire(self):
        """
        Renvoie l'aire du Rectangle.

        Cette méthode définit la méthode abstraite `Forme.area()`,
        pour les Rectangles uniquement.
        """

        return self.longueur * self.largeur

    def allonger(self, facteur):
        """Multiplie la *longueur* du Rectangle par un facteur"""

        self.longueur *= facteur

Il est traditionnel de commencer les noms de classes avec une majuscule (Forme), et les noms d'instances de classes (les variables) avec une minuscule (rosie).

Exemples

Études de cas

Exercices :

II-J. Entrées-sorties

II-J-1. Intéractif

Comme nous avons pu le voir précédemment, l'affichage à l'écran se fait par print, la lecture du clavier par raw_input.

II-J-2. Fichiers

La gestion des fichiers (lecture et écriture) se fait à partir de la fonction open() retournant un objet de type file :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
# ========== ÉCRITURE ==========
outfile = open("carres.dat", 'w') # Ouverture du fichier "carres.dat" en écriture
for i in range(1, 10):
    outfile.write("{}  {}\n".format(i, i**2)) # Noter la présence du '\n' (non automatique)
outfile.close()                   # Fermeture du fichier (nécessaire)

# ========== LECTURE ==========
infile = open("carres.dat")  # Ouverture du fichier "carres.dat" en lecture
for line in infile:          # Boucle sur les lignes du fichier
    if line.strip().startswith('#'): # Ne pas considérer les lignes "commentées"
        continue
    try:                     # Essayons de lire 2 entiers sur cette ligne
        x, x2 = [ int(tok) for tok in line.split() ]
    except ValueError:       # Gestion des erreurs
        print "Cannot decipher line '{}'".format(line)
        continue
    print "{}**3 = {}".format(x, x**3)

II-K. Éléments passés sous silence

Cette (brève) introduction à Python se limite à des fonctionnalités relativement simples du langage. De nombreuses fonctionnalités du langage n'ont pas été abordées :

  • v ;
  • arguments anonymes : *args et **kwargs ;
  • fonction anonyme : lambda x, y: x + y ;
  • itérateurs et générateurs : yield ;
  • gestion de contexte : with ... as (PEP 243) ;
  • décorateurs : fonction sur une fonction ou une classe (@staticmethod, etc.) ;
  • héritages multiples et méthodes de résolution ;
  • etc.

Ces fonctionnalités peuvent évidemment être très utiles, mais ne sont généralement pas strictement indispensables pour une première utilisation de Python dans un contexte scientifique.

II-K-1. Python 3.x

Pour des raisons historiques autant que pratiques(4), ce cours présente le langage Python dans sa version 2.x. Pourtant, le développement actuel de Python se fait uniquement sur la branche 3.x, qui constitue une remise à plat non rétrocompatible du langage, et la branche 2.x ne sera a priori plus supporté au-delà de 2020 (PEP 466). Il est donc préférable, si vous vous lancez dans un développement substantiel, de passer aussi rapidement que possible à Python 3 (voir Python 2 vs. python 3Python 2 vs. Python 3).

Python 3 apporte quelques changements fondamentaux, notamment :

  • print n'est plus un mot-clé mais une fonction : print(...) ;
  • l'opérateur / ne réalise plus la division euclidienne entre les entiers, mais toujours la division réelle ;
  • un support complet (mais encore complexe) des chaînes Unicode ;
  • un nouveau système de formatage des chaînes de caractères (f-string du PEP 498 à partir de Python 3.6).

précédentsommairesuivant
ou from __future__ import braces :-)
La notion de « portée » est plus complexe, je simplifie…
Par opposition au LBYLLook Before You Leap du C/C++, basé sur une série exhaustive de tests a priori.
De nombreuses distributions Linux sont encore basées sur Python 2.7 par défaut.

Copyright © 2017 Yannick Copin. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.