ENSTA Bretagne 2, rue François Verny 29806 BREST cedex FRANCE Tel +33 (0)2 98 34 88 00 www.ensta-bretagne.fr Support de cours Formation Continue Avril 2014 Python de la calculatrice à la programmation structurée Arnaud Coatanhay et Christophe Osswald [email protected] 1. Python : langage informatique 2. Une calculatrice polyvalente 3. Structures algorithmiques 4. Bibliothèques 5. Langage objet 6. Pour aller plus loin Index Bibliographie 5 11 50 74 87 96 105 109 Table des matières Le Python, c’est bon Remerciements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 4 1 Python : quelle place parmi les langages informatiques 1.1 Histoire du langage . . . . . . . . . . . . . . . . . . . . . . 1.2 Caractéristiques du langage . . . . . . . . . . . . . . . . . 1.3 Python et les autres langages . . . . . . . . . . . . . . . . 1.4 Développer avec Python(x,y) . . . . . . . . . . . . . . . . 1.5 Développer sans Python(x,y) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 5 6 6 7 9 2 Une calculatrice polyvalente 2.1 Manipuler des nombres . . . 2.2 La bibliothèque Numpy . . 2.3 Définition des matrices . . . 2.4 Manipulation des matrices . 2.5 Tracer des courbes . . . . . . . . . . . . . . . . . . . . 11 11 17 21 25 33 . . . . . . . 50 51 53 55 58 60 68 69 . . . . 74 74 74 75 82 . . . . . . . . . . 3 Structures algorithmiques 3.1 Types de données dynamiques . 3.2 Boucles et conditionnelles . . . 3.3 Définition de fonction . . . . . 3.4 Lancement d’un programme . . 3.5 Algorithmes de tri . . . . . . . 3.6 Modules . . . . . . . . . . . . . 3.7 Simulations numériques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 Bibliothèques de haut niveau 4.1 Gestion du temps par time . . . . . 4.2 Utilisation des fichiers en mode texte 4.3 PIL : traitement d’image . . . . . . . 4.4 Sympy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 Langage objet 87 5.1 Variables d’instance . . . . . . . . . . . . . . . . . . . . . . . . 88 5.2 Méthodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 2 5.3 5.4 5.5 5.6 5.7 5.8 Constructeur . . . . . . . . . . . . Destructeur : ramasse-miette . . . Surcharge d’opérateur . . . . . . . Documention et interrogation . . . Héritage . . . . . . . . . . . . . . . Exemple : géométrie par les objets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 89 90 90 91 92 6 Pour aller plus loin 6.1 Analyse de texte par expressions régulières 6.2 Interrogation du web et format HTML . . . 6.3 Interaction avec le système d’exploitation . 6.4 Utilisation de fichiers par pickle . . . . . . 6.5 Bases de données . . . . . . . . . . . . . . . 6.6 Passage de Python 2.x à Python 3.x . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 . 96 . 97 . 98 . 98 . 99 . 100 Index 105 Bibliographie 109 3 Le Python, c’est bon Sommaire Remerciements 4 C e fascicule s’adresse à des scientifiques – avec une certaine aisance en mathématiques – plus qu’à des développeurs – qui connaîtraient déjà d’autres langages que Python. C’est pourquoi le second chapitre traite de la représentation des nombres en Python comme des matrices par numpy et va jusqu’au tracé des fonctions par une bibliothèque de haut niveau, matplotlib. Il est construit pour suivre sensiblement le même ordre que les quatre demies journées du stage de formation : le troisième chapitre fait passer de la calculatrice à la programmation, et le quatrième présente des fonctionnalités de Python de haut niveau qui seront utilisées dans le cadre du stage Le cinquième chapitre permet d’entrer dans la programmation objet, incoutournable lorsque le projet dépasse le millier de lignes. Le sixième présente des fonctionnalités qui sont utiles dans de nombreuses situations, mais un peu moins durant ces deux jours. Remerciements Les auteurs tiennent à remercier Irvin Probst, qui a conduit une large part des étudiants de l’ENSTA Bretagne à pratiquer l’analyse numérique et le traitement de données en Python. Une grande partie de ce document n’aurait pas pu voir le jour sans ses conseils avisés. Les auteurs remercient Rodéric Moitié, longtemps en charge de l’enseignement de l’algorithmique et de la recherche opérationnelle à l’ENSTA Bretagne, et qui a contribué largement à la section sur les tris. Les auteurs remercient également les participants des éditions précédentes pour leur enthousiasme et leurs questions avisées. 4 1 Python : quelle place parmi les langages informatiques Sommaire 1.1 1.2 1.3 1.4 1.5 1.1 Histoire du langage Caractéristiques du langage Python et les autres langages Développer avec Python(x,y) Développer sans Python(x,y) 5 6 6 7 9 Histoire du langage L a première version de Python date de 1991, développée par Guido Van Rossum. Le nom du langage est un hommage au Monty Python’s Flying Circus. Il utilise une syntaxe très légère, inspirée d’ABC. Le langage dispose de très peu de types de données, mais est riche en structures de données dynamiques, bien intégrées à la syntaxe. La license sous laquelle il est distribué se stabilise en 2001 sur une licence libre compatible avec la GPL 1 . Il s’agit d’un logiciel libre : vous pouvez modifier votre interpréteur Python et distribuer cette version modifiée. Toutefois, cette licence n’entraîne aucune contrainte quant à la licence qui régit les programmes écrits en Python. Le modèle objet est unifié avec la version 2.2 : les types de base (int, float, bool) deviennent des objets comme les autres. Ils ont des méthodes, et il est possible d’en hériter. La dernière version de cette branche est la 2.7.6 de novembre 2013. La version 3.0 vise à éliminer des redondances dans les fonctionnalités de Python. Il s’agit de diminuer le nombre de façons de réaliser une même opération, et donc la complexité du langage comme de sa machine virtuelle. Une conséquence est que la version 3.0 n’est pas compatible avec les versions précédentes : elle ne peut pas exécuter du code Python 2.x. Certaines bibliothèques 1. La GNU General Public License est le plus importante des licenses sous lesquelles sont distribués les logiciels libres ; elle est écrite par la Free Software Foundation. La license de Python est la Python Software Foundation License (PSFL), inspirée de la licence BSD de Berkeley. 5 scientifiques n’ont pas encore fait cette transition et la dernière version de Python(x,y) utilise actuellement Python 2.7.6. Il existe un script, 2to3, qui permet de transformer automatiquement du code de Python 2.x en Python 3. 1.2 Caractéristiques du langage Python est un langage objet, où tout est objet, y compris ce qui est communément appelé type de base : entiers, réels, booléens. Il permet l’héritage multiple. Python est un langage de haut niveau, où nombre de structures de données dynamiques (listes et tables de hachages notamment) sont intégrées à la syntaxe du langage. De nombreuses bibliothèques fournissent des fonctions évoluées. Il n’est pas prévu de contrôler finement le comportement du processeur ou du compilateur avec ce langage 2 . Python est un langage à typage dynamique. Le développeur n’a pas à déclarer les variables avant de leur affecter une valeur : la création de la variable se fait au moment où elle est utile. Il n’a pas non plus à déclarer son type : lors de son utilisation, les opérateurs (qui sont en fait des méthodes des opérandes) en testent le type pour vérifier s’ils sont dans leurs conditions d’utilisation. Python est un langage à typage fort : lorsqu’un opérateur reçoit deux opérandes incompatibles, une exception est levée et l’opération n’est pas exécutée. Un langage à typage faible aurait tenté une conversion de l’un des opérandes vers un type compatible et effectué l’opération, au risque d’avoir un résultat inattendu (4+"2" doit-il fournir la chaîne "42", l’entier 6 ou une erreur ?) Python permet la glue logicielle, et peut notamment appeler des fonctions présentes dans des fichiers .o issus de programmes C. Des bindings permettent la traduction des données du C en objets Python, et inversement. Python est un langage à ramasse-miette : de l’espace mémoire est réservé pour les objets lorsqu’ils sont créés, et la machine virtuelle se charge de libérer cet espace lorsqu’elle se rend compte que l’objet ne peut plus être utilisé par le programme. Cela libère le programmeur de la gestion de la mémoire, au risque parfois d’en consommer trop (si les miettes ne sont pas identifiées comme telles) et un surcoût de temps d’exécution. 1.3 Python et les autres langages C, Fortran, Ada, Pascal/Delphi sont des langages compilés : un compilateur doit transformer un fichier source, généralement assez indépendant de la plate-forme matérielle, en un code exécutable, adapté au processeur et à l’architecture voulus. Java est un langage semi-compilé : un compilateur vérifie la syntaxe du fichier source et le transforme en un byte-code indépendant de la machine, et une machine virtuelle, spécifique à chaque architecture, exécute le byte-code. Matlab, tcsh, bash et les autres shells sont des langages interprétés : le fichier source est exécuté ligne par ligne. Il n’y a pas d’étape intermédiaire entre l’écriture du code et son exécution. Le programme qui se charge d’exécuter chacune des lignes est l’interpréteur. 2. Pas d’équivalent des register ou inline du C, donc. 6 Perl est un langage interprété précompilé, dont la syntaxe est proche du shell : l’interpréteur vérifie d’abord que le programme est syntaxiquement correct, puis l’exécute. Python est un langage objet interprété précompilé. Une première passe du programme python sur le script vérifie la grammaire du code et crée un fichier .pyc. Cette forme précompilée est sauvegardée lors de l’exécution du script. Dans un deuxième temps ce code précompilé est exécuté par la composante de python qui joue le rôle de machine virtuelle. 1.4 Développer avec Python(x,y) L’environnement de développement intégré Python(x,y) regroupe la plupart des bibliothèques mathématiques de Python et l’éditeur de texte spyder (Scientific PYthon Development EnviRonment). Il inclut par défaut les bibliothèques scientifiques numpy, scipy et matplotlib, ainsi que le vérificateur de code pylint. Il gère l’installation de plusieurs autres bibliothèques, comme sympy, dédiée au calcul symbolique. Figure 1.1 – Fenêtre spyder au démarrage Sous Windows, il suffit de télécharger l’installeur sur le site de Python(x,y) : http://code.google.com/p/pythonxy/ et de sélectionner éventuellement des modules additionnels. L’interpréteur Python est récupéré automatiquement. 7 Sous Linux 3 il suffit de sélectionner le paquet spyder : l’interpéteur python ainsi que les bibliothèques numpy, scipy et matplotlib en dépendent et seront installées également. Il peut être utile de sélectionner les autres bibliothèques souhaitées, comme sympy. La partie de gauche est l’éditeur de code source. L’éditeur affecte une couleur au texte en fonction de son rôle dans le programme : bleu pour les mots-clés du langage, violet pour les fonctions et les constantes, noir pour les opérateurs et les variables, gris pour les commentaires et vert pour la documentation. Le terme surligné en jaune est sélectionné à la souris : on voit à quels endroits du code il est utilisé. La ligne du curseur est surlignée en rose. La position exacte du curseur (ligne 12, colonne 12) est rappelée en pied de fenêtre. On peut exécuter le fichier en cours d’édition par Menu/Exécution/Exécution ou simplement par F5. La partie en haut à droite permet d’afficher de la documentation sur des fonctions existantes ou de contrôler le contenu des variables du programme. La partie en bas à droite est un interpréteur en mode interactif : le code python qui y est tapé est immédiatement exécuté. C’est cette partie qui permet d’utiliser Python comme une calculatrice. L’interpréteur Python de spyder est configuré pour rendre immédiatement disponibles les bibliothèques les plus courantes : >>> import numpy as np >>> import scipy as sp # NumPy ( multidimensional arrays , linear algebra , ...) # SciPy ( signal and image processing library ) >>> import matplotlib as mpl # Matplotlib ( 2D / 3D plotting library ) >>> import matplotlib . pyplot as plt # Matplotlib ’s pyplot : MATLAB - like syntax >>> from pylab import * # Matplotlib ’s pylab interface >>> ion () # Turned on Matplotlib ’s interactive mode Les trois chevrons >>> constituent l’invite de commande. Ce qui suit est la ligne de code à exécuter. Si la ligne est un début de bloc, l’invite de commande devient ... jusqu’à la fin du bloc (une ligne vide), lequel est alors exécuté. >>> >>> >>> ... ... ... ... >>> >>> ... ... ... 2 3 N = 100 premiers = [ x for x in range (0 , N ) ] for n in range (2 , int ( N ** 0 . 5 ) ) : if premiers [ n ] ! = 0 : for i in range ( 2 *n , N , n ) : premiers [ i ] = 0 premiers [ 1 ] = 0 for i in premiers : if premiers [ i ] ! = 0 : print i , 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 3. Ubuntu, Debian ou Mint utilisent des paquets .deb gérés par synaptic. Les architectures issues de Redhat utilisent des paquets .rpm ; la procédure peut varier. 8 1.5 Développer sans Python(x,y) À partir du moment où Python est installé sur un ordinateur (Linux, Windows ou MacOS), taper “python” dans un terminal donne accès à l’interpréteur interactif de Python. Il ne dispose toutefois pas des bibliothèques mathématique, il faut les importer (cf. liste de import de la page 8). On peut alors utiliser Python comme une calculatrice évoluée, à la manière d’un octave en ligne de commande. On peut également lancer un programme Python écrit dans un fichier texte simulation.py par : python simulation.py Le répertoire contient maintenant un nouveau fichier : simulation.pyc. Il s’agit d’un fichier binaire, qu’on ne peut pas éditer à la main. On pourrait même mesurer qu’une seconde exécution se fait plus rapidement : l’étape de précompilation n’a pas à être faite. Si un programme nécessite des paramètres, on pourra les lui passer par la ligne de commande : python eratosthene.py 200 1 import sys 2 3 4 5 6 7 8 9 10 11 12 N = int(sys.argv[1]) premiers =[x for x in range(0,N)] for n in range(2,int(N**0.5)): if premiers[n] !=0: for i in range(2*n, N, n): premiers[i]=0 premiers[1]=0 for i in premiers: if premiers[i] !=0: print i, Figure 1.2 – Crible d’Ératosthène, nécessite un paramètre entier D’autres éditeurs ou environnements de développement permettent d’écrire du Python, voire de traquer les bogues dans un programme Python : eclipse/PyDev : L’IDE eclipse permet d’écrire du code dans de nombreux langages. Il fournit une coloration syntaxique, de l’indentation automatique, de la complétion automatique, et un contrôle de l’exécution pour le débogage. Il gère les ensembles de fichier sources sous la forme de projet et est adapté aux développements de grands logiciels en équipe. Initialement écrit pour Java (en Java), le module PyDev permet de l’utiliser pour Python. Il s’agit de l’IDE le plus utilisé ces dernières années, et de nombreuses Écoles l’ont adopté. Il est disponible sur toutes les plate-formes. GNU emacs est un éditeur de code informatique universel 4 , adapté à tous les langages. Son interface est essentiellement sous forme de texte, et il 4. Le code LaTeX de ce fascicule est écrit sous emacs, ainsi qu’une large part des codes Python. 9 s’appuie fortement sur des raccourcis clavier qui lui sont spécifiques. Il est disponible sur toutes les plate-formes, mais mieux intégré au monde linux. Avant l’arrivée d’éclipse, il s’agissait de l’éditeur le plus utilisé dans la plupart des Écoles. gedit : éditeur de texte de Gnome, gestionnaire de fenêtre linux. Il gère la coloration syntaxique pour de nombreux langages, mais pas la complétion automatique ou l’exécution. Il a l’avantage d’être léger et intuitif. Il faut ensuite exécuter le fichier produit en ligne de commande. Sur la distribution Mint, c’est son proche cousin pluma qui est disponible. Pour de petits programmes (cas typiques de l’initiation à l’informatique) ce peut être l’éditeur le plus adapté. 10 2 Une calculatrice polyvalente Sommaire 2.1 2.2 2.3 2.4 2.5 2.1 Manipuler des nombres 2.1.1 Types numériques 2.1.2 Opérations sur les nombres La bibliothèque Numpy 2.2.1 Importation d’une bibliothèque 2.2.2 Les types sous Numpy 2.2.3 Quelques fonctions numériques sous Numpy Définition des matrices 2.3.1 Structures de base 2.3.2 Le type matrix sous Numpy 2.3.3 Le type array sous Numpy Manipulation des matrices 2.4.1 Les bases 2.4.2 Construction des matrices 2.4.3 Matrices particulières 2.4.4 Opérations 2.4.5 Sous-bibliothèque linalg 2.4.6 Fonctions numériques de Numpy sur les matrices 2.4.7 La bibliothèque Scipy Tracer des courbes 2.5.1 Première approche 2.5.2 Des graphiques plus élaborés 11 12 15 17 17 18 19 21 21 23 23 25 25 27 29 30 31 32 32 33 33 45 Manipuler des nombres E en première approche, le langage Python peut être vu comme un ensemble de commandes simples à utiliser. En ce sens, il peut jouer le rôle d’une calculatrice scientifique classique. Pour s’en convaincre il suffit de lancer la séquence triviale suivante : >>> a = 1 >>> b = 2 >>> c = a + b Pour avoir le résultat de l’opération, on prolongera simplement par : >>> print c 3 11 ou encore plus simplement par : >>> c 3 2.1.1 Types numériques En mathématiques, les nombres appartiennent à des ensembles tels que N, Z, Q, R ou C. En Python (comme dans beaucoup de langages informatiques) les nombres seront du type : int (integer) correspond à un entier (N ou Z) stocké sur une partie limitée de la mémoire de l’ordinateur. Il ne peut donc pas être aussi petit (négatif) ou aussi grand que l’on veut. Pour une machine (dépend du microprocesseur et du système d’exploitation) de 32 bits (4 octets), un entier sera compris entre −231 et 231 − 1 (soit entre -2 147 483 648 et 2 147 483 647). Pour une machine de 64 bits, un entier sera compris entre −263 et 263 − 1 (soit entre -9 223 372 036 854 775 808 et 9 223 372 036 854 775 807). long (long integer) le type entier long est équivalent au type entier sauf qu’il n’est pas limité à nombre d’octets (ou de bits prédéterminé). À l’extrême, un nombre peut prendre la quasi totalité de la mémoire de l’ordinateur. Il est clair alors que ce nombre peut être considérable. float le type flottant permet de représenter des nombres à virgule. Il est codé en mémoire sur 8 octets (64 bits). Il est représenté sous la forme nombre = ±1.mantisse∗2±exposant . La mantisse est écrite avec 52 chiffres binaires, et l’exposant avec 10 ; il y a deux bits de signe. La précision maximale est donc de l’ordre de 2 × 10−16 . Des valeurs spéciales permettent de représenter −∞, +∞ et Nan, not a number, souvent issu d’une forme indéterminée. complex le type complexe correspond à une structure naturellement composée de deux flottants (partie réelle et imaginaire) sur 2 x 8 = 16 octets. bool le type booléen correspond à l’algèbre booléenne et ne prend que deux valeurs : True/False. Il est codé sur un bit. En pratique, pour des applications basiques, le typage est la plupart du temps transparent pour l’utilisateur car Python interprète dynamiquement le type des variables. Par exemple sur une machine 32bits, on peut lancer la séquence : >>> a = 2147483647 >>> a 2147483647 >>> b = a + 1 >>> b 2147483648L >>> print b 2147483648 Le nombre a est de type entier, mais il atteint le maximum possible pour des entier (à adapter pour une machine 64 bits). Si on ajoute 1 à la variable a, on est obligé de stoker le résultat dans une variable de type long. On constate qu’à la fin du nombre porté par la variable b qu’il y a un L (ce qui indique le type long). On remarquera que l’information de type (le L) n’apparaît pas avec 12 la commande print : print utilise str(b) pour convertir l’entier en chaîne de caractère ; écrire directement b fait appel à repr(b). Si on retire 1 à b il conserve le type long, mais si on ajoute 1 à la variable a, cette variable change dynamiquement de type. >>> b = b - 1 >>> b 2147483647L >>> a = a + 1 >>> a 2147483648L On peut aussi créer un nombre entier énorme qui sera bien évidemment de type long (remarque : ** est l’opérateur de mise à la puissance) : >>> c = 2 ** 10000 >>> c 19950631168807583848837421626835850838234968318861924548520089498 52943883022194663191996168403619459789933112942320912427155649134 94137811175937859320963239578557300467937945267652465512660598955 20550086918193311542508608460618104685509074866089624888090489894 83800925394163325785062156830947390255691238806522509664387444104 67598716269854532228685381616943157756296407628368807607322285350 91641476183956381458969463899410840960536267821064621427333394036 52556564953060314268023496940033593431665145929777327966577560617 25820314079941981796073782456837622800373028854872519008344645814 54650557929601414833921615734588139257095379769119277800826957735 67444412306201875783632550272832378927071037380286639303142813324 14016241956716905740614196543423246388012488561473052074319922596 11796250130992860241708340807605932320161268492288496255841312844 06153673895148711425631511108974551420331382020293164095759646475 60104058458415660720449628670165150619206310041864222759086709005 74606417856951911456055068251250406007519842261898059237118054444 78807290639524254833922198270740447316237676084661303377870603980 34131971334936546227005631699374555082417809728109832913144035718 77524768509857276937926433221599399876886660808368837838027643282 77517227365757274478411229438973381086160742325329197481312019760 41782819656974758981645312584341359598627841301281854062834766490 88690521047580882615823961985770122407044330583075869039319604603 40497315658320867210591330090375282341553974539439771525745529051 02123109473216107534748257407752739863482984983407569379556466386 21874569499279016572103701364433135817214311791398222983845847334 44027096418285100507292774836455057863450110085298781238947392869 95408343461588070439591189858151457791771436196987281314594837832 02081474982171858011389071228250905826817436220577475921417653715 68772561490458290499246102863008153558330813010198767585623434353 89554091756234008448875261626435686488335194637203772932400944562 46923254350400678027273837755376406726898636241037491410966718557 05075909810024678988017827192595338128242195402830275940844895501 46766683896979968862416363133763939033734558014076367418777110553 84225739499110186468219696581651485130494222369947714763069155468 21768287620036277725772378136533161119681128079266948188720129864 36607685516398605346022978715575179473852463694469230878942659482 17008051120322365496288169035739121368338393591756418733850510970 27161391543959099159815465441733631165693603112224993796999922678 17323580231118626445752991357581750081998392362846152498810889602 32244362173771618086357015468484058622329792853875623486556440536 96262201896357102881236156751254333830327002909766865056855715750 55167275188991941297113376901499161813151715440077286505731895574 50920330185304847113818315407324053319038462084036421763703911550 63978900074285367219628090347797453332046836879586858023795221862 91200807428195513179481576244482985184615097048880272747215746881 13 31594750409732115080498190455803416826949787141316063210686391511 68 17 74 304 79 25 96 709 37 6L Pour les booléens (| et & indiquent respectivement ou et et), on peut lancer la séquence : >>> a = True >>> b = False >>> a | b True >>> a & b False Pour les nombres complexes, on peut tester : >>> a = 1 . 5 + 3 . j >>> b = 1 . 5 >>> a - b 3j >>> a * b ( 2 . 25 + 4 . 5j ) >>> c = 1 . + 1 . j >>> a * c ( - 1 . 5 + 4 . 5j ) >>> ( 1 + 4j ) ** ( 1 / ( 2 + 3j ) ) ( 1 . 67585169460166 - 0 . 20706889169199882j ) >>> 1j * complex (0 , 1 ) ( - 1 + 0j ) En réalité, il possible d’utiliser la structure des nombres complexes de façon plus évoluée. Par exemple : >>> a = 1 . 5 + 3 . j >>> a . real 1.5 >>> a . imag 3.0 >>> a . conjugate () ( 1 . 5 - 3j ) Enfin, il est possible de modifier explicitement le type des variables en utilisant les fonctions de conversion : int(a) long(a) float(a) complex(a) complexe(a,b) Conversion Conversion Conversion Conversion Conversion de a a en a en a en de a en type entier type entier long type flottant type complexe a+0j et de b en type complexe a+bj Sous Python, il existe la fonction type() qui donne le type d’une variable. Par exemple : >>> a = 1 >>> type ( a ) < type ’ int ’> >>> a = 1 . >>> type ( a ) < type ’ float ’> >>> a = 2 ** 10000 >>> type ( a ) < type ’ long ’> >>> a = 3 . + 4 . j 14 >>> type ( a ) < type ’ complex ’> 2.1.2 Opérations sur les nombres Opérateurs arithmétiques Python ne possède que très peu d’opérations arithmétiques pour manipuler les nombres. À ce stade, Python principalement se résume à une calculatrice élémentaire : + − ∗ / // % ∗∗ Addition Soustraction Multiplication Division Division entière Division modulaire Puissance La nature des opérations arithmétiques dépend du type des variables. Voici une séquence Python lancée sous une fenêtre de commande : >>> >>> >>> >>> 1 >>> >>> 3.0 >>> >>> 2.0 >>> 1.5 >>> 1.0 a=3 b=2 c=a/b print c a=3. print a b=2.0 print b a/b a // b L’opérateur // tronque la valeur obtenue à l’entier inférieur. Il renvoie un float ou un entier (int ou long) selon le type des opérandes. Avec des entiers, la division / devient une division entière. Il est important de souligner que dans les versions Python 3.*, l’opérateur / est systématiquement une vraie division. Sur des entiers, / et // ont le même comportement en Python 2.*. Attention, il est possible de modifier ce fonctionnement par défaut. Par exemple, la même séquence lancée sous spyder donne : >>> >>> >>> >>> 1.5 a=3 b=2 c=a/b print c On parle alors de vraie division. 15 Raccourcis pour les opérateurs arithmétiques Dans une séquence de code, on peut affecter une variable à une valeur, effectuer une opération arithmétique sur cette variable et finalement affecter la nouvelle valeur obtenue à la variable. Par exemple : >>> >>> >>> >>> 2 a=1 b=1 a=a+b print a En Python, il est possible d’écrire l’opération arithmétique de façon plus compacte : >>> >>> >>> >>> 2 a=1 b=1 a+=b print a Ceci est également vrai pour les autres opérateurs arithmétiques : a+=b a-=b a*=b a/=b a**=b a%=b a=a+b a=a-b a=a*b a=a/b a=a**b a=a%b Quelques fonctions supplémentaires De base, Python fournit quelques fonctions numériques autres que les opérations arithmétiques : abs(a) max(...) min(...) round(a,n) cmp(a,b) Valeur absolue de a Plus grande valeur d’une suite de nombres Plus petite valeur d’une suite de nombres Arrondi de la variable a au niveau de la ne décimale −1 si a < b Comparaison de a et de b : 0 si a = b 1 si a > b Par exemple : >>> a = - 1 >>> abs ( a ) 1 >>> a = 1 >>> b = 3 . 4 >>> c = 10 >>> max (a ,b , c ) 10 >>> min (a ,b , c ) 1 >>> a = 3 . 141592654 >>> round (a , 4 ) 3 . 1416 16 >>> round (a , 2 ) 3 . 14 >>> round (a , 0 ) 3.0 Opérateurs de comparaison Python peut aussi vérifier si une comparaison entre deux nombres est vraie ou fausse ce qui renvoie une valeur booléenne. Les opérateurs standards de comparaison sont : < > <= >= == != Plus petit que Plus grand que Plus petit que ou égal à Plus grand que ou égal à Égal à Pas égal à Par exemple : >>> a = 5 >>> b = 4 . 9999 >>> a < b False >>> b > a False >>> a > b True >>> a = = b False >>> a ! = b True 2.2 La bibliothèque Numpy Il est impossible de donner une liste exhaustive de toutes les bibliothèques relatives à Python. Toutefois, pour les applications scientifiques, la bibliothèque Numpy apparaît incontournable. 2.2.1 Importation d’une bibliothèque L’importation d’une bibliothèque se fait grâce à la commande import. Pour accéder à une fonction particulière de la bibliothèque, on peut taper nom_de_la_bibliothèque.nom_de_la_fonction(). Par exemple pour la fonction sin() : >>> import numpy >>> numpy . sin ( 3 . 141592654 ) - 4 . 1020685703470686e - 10 Pour éviter de retaper le nom complet de la bibliothèque (qui dans certain peut être assez long), on peut modifier le nom de la bibliothèque en utilisant le terme as. Par exemple, ici on renomme numpy par np : >>> import numpy as np >>> np . sin ( 3 . 141592654 ) - 4 . 1020685703470686e - 10 17 On peut se passer complètement du rappel du nom de la bibliothèque, en spécifiant directement les fonctions qu’on désire importer avec le terme from : >>> from numpy import sin >>> sin ( 3 . 141592654 ) - 4 . 1020685703470686e - 10 On peut même importer directement toutes les fonctions d’une bibliothèque avec le symbole * : >>> from numpy import * >>> sin ( 3 . 141592654 ) - 4 . 1020685703470686e - 10 A priori, la dernière solution semble la plus simple car elle évite des écritures répétitives. D’ailleurs par défaut, la fenêtre console de spyder effectue directement l’importation de toutes les fonctions de la bibliothèque numpy. Ainsi, sous spyder, on peut sans import explicite lancer la commande suivante : >>> sin ( 1 . 5 ) 0 . 99749498660405445 Toutefois, l’import systématique de fonction est à déconseiller fortement pour deux raisons. La première est que l’on importe en général une grande quantité de fonctions dont on ne se sert pas réellement. La seconde est qu’en Python, on est amené à importer un nombre conséquent de bibliothèques, dont certaines fonctions peuvent porter le même nom. Par exemple, la bibliothèque math existe et possède également la fonction sin(). D’après la séquence de code suivante, les deux fonctions sin() semblent se comporter de la même façon sur un nombre, mais on constatera qu’elles sont très différentes dès lors qu’elles s’appliquent sur une liste : >>> import numpy as np >>> import math as m >>> a = 3 . 14 >>> np . sin ( a ) 0 . 0015926529164868282 >>> m . sin ( a ) 0 . 0015926529164868282 >>> b = [ 1 .5 , 3 . 14 ] >>> np . sin ( b ) array ( [ 0 . 99749499 , 0 . 00159265 ] ) >>> m . sin ( b ) Traceback ( most recent call last ) ~ : File " < stdin > " , line 1 , in < module > TypeError ~ : a float is required 2.2.2 Les types sous Numpy L’importation de la libraire Numpy augmente le nombre des types de possible. En plus des entiers classiques (int), on peut considérer les entiers positifs (uint). On peut aussi spécifier le nombre de bits utilisé pour le codage. La liste des types devient alors : bool : True ou False ; toute valeur numérique différente de 0 est assimilée à True ; 18 int : le type entier par défaut de Python ; sa taille n’est pas définie par la norme, et peut correspondre à int32 ou int64 selon les architectures. En cas de dépassement, il est automatiquement remplacé par un long. Sous Python 3.* les types int et long sont fusionnés ; int8 : de -128 à 127, int16 : de -32 768 à 32 767, int32 : de -2 147 483 648 à 2 147 483 647, int64 -9 223 372 036 854 775 808 à 9 223 372 036 854 775 807, uint8 : de 0 à 255, uint16 : de 0 à 65 535, uint32 : de 0 à 4 294 967 295, uint64 : de 0 à 18 446 744 073 709 551 615, float64, float : nombre à virgule flottante sur 8 octets ; correspond au type double du C. float32 : nombre à virgule flottante sur 4 octets ; correspond au type float du C. float16 : nombre à virgule flottante sur 2 octets ; il ne correspond à aucun type du microprocesseur : il permet de gagner de l’espace de stockage, mais pas de vitesse de calcul ; complex128, complex : nombre complexe dont la partie réelle et la partie imaginaire sont des float64, c’est-à-dire des float complex64 : nombre complexe dont la partie réelle et la partie imaginaire sont des float32. 2.2.3 Quelques fonctions numériques sous Numpy La listes des fonctions numériques définies dans numpy est extrêmement importante. On peut ici donner une liste non exhaustive des fonctions les plus classiques (remarque : les termes entre [ et ] sont des paramètres optionnels). Trigonométrie sin(x[, out]) cos(x[, out]) tan(x[, out]) arcsin(x[, out]) arccos(x[, out]) arctan(x[, out]) hypot(x1, x2[, out]) arctan2(x1, x2[, out]) degrees(x[, out]) radians(x[, out]) unwrap(p[, discont, axis]) deg2rad(x[, out]) rad2deg(x[, out]) Sinus Cosinus Tangente Arcsinus Arccosinus Arctangente Hypothénuse en fonction des deux autres cotés Arctangente du rapport x1/x2 (quadrant selon x1 et x2) Conversion de radians vers degrés Conversion de degrés vers radians Déroulement de la phase Conversion de degrés vers radians Conversion de radians vers degrés 19 Trigonométrie hyperbolique sinh(x[, out]) cosh(x[, out]) tanh(x[, out]) arcsinh(x[, out]) arccosh(x[, out]) arctanh(x[, out]) Sinus hyperbolique Cosinus hyperbolique Tangente hyperbolique Arcsin hyperbolique Arccosinus hyperbolique Arcsinus hyperbolique Arrondis around(a[, decimals, out]) rint(x[, out]) fix(x[, y]) floor(x[, out]) ceil(x[, out]) trunc(x[, out]) Arrondi à une décimale près Entier le plus proche Arrondi à 0 décimale Arrondi à la valeur inférieure Arrondi à la valeur supérieure Arrondi à l’entier le plus proche de zéro Exponentielles et logarithmes exp(x[, out]) expm1(x[, out]) exp2(x[, out]) log(x[, out]) log10(x[, out]) log2(x[, out]) log1p(x[, out]) Exponentielle Exponentielle moins 1 Exponentielle de base 2 Logarithme naturel Logarithme de base 10 Logarithme de base 2 Logarithme de x + 1 Fonctions spéciales i0(x) sinc(x) Bessel modifiée de première espèce d’ordre 0 Sinus cardinal Beaucoup d’autres fonctions spéciales existent dans Numpy mais elles appartiennent à des sous-librairies (voir sections 3.6, 3.7.2 à 3.7.4, et chapitre 4). Autres fonctions signbit(x[, out]) copysign(x1, x2[, out]) frexp(x[, out1, out2]) ldexp(x1, x2[, out]) Indication de la présence d’un bit de signe Copie du signe de x2 sur x1 Décomposition x = out1 ∗ 2out2 Calcul x1 ∗ 2x2 20 Arithmétique add(x1, x2[, out]) reciprocal(x[, out]) negative(x[, out]) multiply(x1, x2[, out]) divide(x1, x2[, out]) power(x1, x2[, out]) subtract(x1, x2[, out]) true_divide(x1, x2[, out]) floor_divide(x1, x2[, out]) fmod(x1, x2[, out]) mod(x1, x2[, out]) modf(x[, out1, out2]) remainder(x1, x2[, out]) Addition Inversion (Attention cas entier) Opposé Multiplication Division Puissance x1x2 Soustraction Vraie division (même entiers) Quotient division entière (même flottants) Reste division entière Reste division entière Partie décimale et partie entière Reste division entière Nombres complexes angle(z[, deg]) real(val) imag(val) conj(x[, out]) Argument Partie réelle Partie imaginaire Conjugué Divers sqrt(x[, out]) square(x[, out]) absolute(x[, out]) fabs(x[, out]) sign(x[, out]) maximum(x1, x2[, out]) minimum(x1, x2[, out]) nan_to_num(x) real_if_close(a[, tol]) Racine carrée Mise au Carré Valeur absolue Valeur absolue Signe (-1,0,1) Maximum Minimum Remplacement de nan et de inf Élimination des petites parties imaginaires Remarque. numpy.nan (Not A Number) et numpy.inf (Infini) sont définis dans la bibliothèque Numpy. 2.3 Définition des matrices 2.3.1 Structures de base En partant des types fondamentaux, il est possible de créer des matrices et des vecteurs en utilisant le concept de liste (cette notion sera détaillée en section 3.1). Une liste se crée en utilisant [ ] : >>> >>> 1.3 >>> 2.0 >>> 7.6 a = [ 1 .3 , 2 .0 , 7 . 6 ] a[0] a[1] a[2] 21 On notera que pour une liste de taille n, l’indice varie de 0 à n − 1. Pour créer une matrice, il est possible de créer une liste de liste : >>> b = [ [1 ,2 , 3 ] ,[4 ,5 , 6 ] ,[7 ,8 , 9 ] ] >>> print b [ [1 , 2 , 3 ] , [4 , 5 , 6 ] , [7 , 8 , 9 ] ] >>> b [ 1 ] [4 , 5 , 6 ] >>> b [ 1 ] [ 2 ] 6 Deux fonctions très utiles pour les listes de nombres : range() la fonction range() permet de générer une liste de nombres entiers avec un pas régulier. Tous les paramètres doivent être des entiers. len() donne la longueur d’une liste. Ces deux fonctions peuvent donner lieu à la séquence suivante : >>> range ( 10 ) [0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ] >>> range (6 , 9 ) [6 , 7 , 8 ] >>> range ( 1 .2 , 5 ) Traceback ( most recent call last ) ~ : File " < stdin > " , line 1 , in < module > TypeError ~ : range () integer start argument expected , got float . >>> range (1 , 10 , 2 ) [1 , 3 , 5 , 7 , 9 ] >>> range ( 10 ,1 , - 2 ) [ 10 , 8 , 6 , 4 , 2 ] >>> a = range (4 , 8 ) >>> len ( a ) 4 >>> b = [ [1 ,2 ,3 , 4 ] ,[5 ,6 ,7 , 8 ] ,[9 , 10 , 11 , 12 ] ] >>> print b [ [1 , 2 , 3 , 4 ] , [5 , 6 , 7 , 8 ] , [9 , 10 , 11 , 12 ] ] >>> len ( b ) 3 Quoi qu’il en soit, il ne sera pas possible de travailler sur des listes ou des listes de listes de façon algébrique. Par exemple, le produit direct de listes conduit à une erreur : >>> a = [ 1 .3 , 2 .0 , 7 . 6 ] >>> b = [ [1 ,2 , 3 ] ,[4 ,5 , 6 ] ,[7 ,8 , 9 ] ] >>> print b [ [1 , 2 , 3 ] , [4 , 5 , 6 ] , [7 , 8 , 9 ] ] >>> b * a Traceback ( most recent call last ) ~ : File " < stdin > " , line 1 , in < module > TypeError ~ : can ’t multiply sequence by non - int of type ’ list ’ Pour réaliser des opérations algébriques, nous sommes condamnés à reprogrammer les opérations fondamentales de l’algèbre linéaire. En ajoutant le fait qu’en Python de base les opérations arithmétiques et fonctions numériques sont limitées, l’emploi de bibliothèques mathématiques est indispensable pour des applications scientifiques. 22 2.3.2 Le type matrix sous Numpy En plus des types de nombre et des fonctions numériques vues précédemment, la bibliothèque numpy apporte également de nouvelles structures. En particulier, il existe la structure matrix qui n’est pas une simple liste. Ainsi, le calcul algébrique suivant : 1 ~v = 2 3 1 0 0 A = 0 −1 0 0 0 2 ~u = A · ~v peut se traduire sous Python par la séquence : >>> v = np . matrix ( [1 ,2 , 3 ] ) >>> print v matrix ( [ [1 , 2 , 3 ] ] ) >>> A = np . matrix ( [ [1 ,0 , 0 ] ,[0 , -1 , 0 ] ,[0 ,0 , 2 ] ] ) >>> print A matrix ( [ [ 1 , 0 , 0 ] , [ 0 , -1 , 0 ] , [ 0, 0, 2]]) >>> A * v Traceback ( most recent call last ) ~ : File " < stdin > " , line 1 , in < module > File " / usr / lib / python2 . 7 / dist - packages / numpy / matrixlib / defmatrix . py " , line 330 , in __mul__ return N . dot ( self , asmatrix ( other ) ) ValueError ~ : objects are not aligned >>> vt = np . transpose ( v ) >>> print vt matrix ( [ [ 1 ] , [2], [3]]) >>> A * vt matrix ( [ [ 1 ] , [-2], [ 6]]) L’avantage de la structure matrix de numpy est que l’opérateur * a le même sens que la multiplication au sens des matrices et que les opérations algébriques deviennent transparentes. Par ailleurs, il existe un grand nombre de fonctions applicables au type matrix : transpose, trace, diagonal, reshape, . . . Par exemple, pour transposer une matrice. On peut écrire sous Python : >>> import numpy as np >>> A = np . matrix ( [ [1 ,2 , 3 ] ,[4 ,5 , 6 ] ,[7 ,8 , 9 ] ] ) >>> np . transpose ( A ) matrix ( [ [1 , 4 , 7 ] , [2 , 5 , 8 ] , [3 , 6 , 9 ] ] ) Toutefois, nous ne développerons pas plus les traitements mathématiques avec le type matrix de numpy. En effet, ce type possède de sérieuses limitations. En particulier, il n’est pas possible de travailler en dimension supérieure à 2. 2.3.3 Le type array sous Numpy Le type array est plus général. Il permet de travailler en dimension quelconque et c’est la structure qui est privilégiée sous numpy. C’est la structure 23 que nous adopterons également lors de la formation. Avec le type array, il est possible de réaliser quasiment les mêmes opérations que nous avons précédemment réalisées avec le type matrix. Toutefois, si nous réécrivons la même séquence que précédemment en utilisant le type array, nous noterons certaines différences : >>> import numpy as np >>> v = np . array ( [1 ,2 , 3 ] ) >>> print v [1 2 3] >>> v array ( [1 , 2 , 3 ] ) >>> A = np . array ( [ [1 ,0 , 4 ] ,[0 , -1 , 0 ] ,[0 ,0 , 2 ] ] ) >>> print A [[ 1 0 4] [ 0 -1 0] [ 0 0 2]] >>> A array ( [ [ 1 , 0 , 4 ] , [ 0 , -1 , 0 ] , [ 0, 0, 2]]) >>> A * v array ( [ [ 1 , 0 , 12 ] , [ 0 , -2 , 0 ] , [ 0, 0, 6]]) >>> vt = np . transpose ( v ) >>> A * vt array ( [ [ 1 , 0 , 12 ] , [ 0 , -2 , 0 ] , [ 0, 0, 6]]) >>> A * A array ( [ [ 1 , 0 , 16 ] , [ 0, 1, 0], [ 0, 0, 4]]) >>> v2 = np . array ( [ [ 1 ,2 , 3 ] ] ) >>> v2 array ( [ [1 , 2 , 3 ] ] ) >>> np . transpose ( v2 ) array ( [ [ 1 ] , [2], [3]]) >>> A * v2 array ( [ [ 1 , 0 , 12 ] , [ 0 , -2 , 0 ] , [ 0, 0, 6]]) >>> A * np . transpose ( v2 ) array ( [ [ 1 , 0 , 4 ] , [ 0 , -2 , 0 ] , [ 0, 0, 6]]) >>> A . dot ( v2 ) Traceback ( most recent call last ) : File " < stdin > " , line 1 , in < module > ValueError : objects are not aligned >>> A . dot ( np . transpose ( v2 ) ) array ( [ [ 13 ] , [-2], [ 6]]) >>> print A . shape , v . shape , v2 . shape , v2 . transpose () . shape (3 , 3 ) (3 ,) (1 , 3 ) (3 , 1 ) Dans l’exemple précédent, A est une matrice 3 × 3, v est un vecteur à une dimension, donc un array dont la forme (shape) est (3), alors que v2 24 est un vecteur ligne, mais un array à deux dimensions, dont la forme est (1,3). Lorsqu’un vecteur mono-dimensionnel est utilisé dans un contexte où il faudrait deux dimensions, il est en général considéré comme un vecteur ligne. La transposée de v est vt qui est identique à v, alors que la transposée de v2 est bien un vecteur colonne de forme (3,1). L’opérateur de multiplication * réalise une multiplication terme à terme ; c’est ainsi que les termes de A*A sont les a2i,j . Les multiplications par v, vt ou v2, qui sont tous des vecteurs ligne, multiplient la ie colonne de A par le ie terme du vecteur. La multiplication par np.transpose(v2) multiplie la ie ligne de la matrice par le terme correspondant du vecteur. Le produit matriciel classique s’obtient par la méthode dot, qui impose au paramètre d’être de la bonne forme : on ne peut pas multiplier A par v2, mais juste par sa transposée. 2.4 Manipulation des matrices À partir de maintenant nous travaillerons (sauf mention explicite) avec le type array de numpy et nous admettrons que l’importation de la bibliothèque numpy est réalisée par : import numpy as np 2.4.1 Les bases Les matrices de type array peuvent se construire à partir des listes de Python. >>> a = np . array ( [1 , 4 , 5 , 8 ] , float ) >>> a array ( [ 1 . , 4 . , 5 . , 8 . ] ) >>> type ( a ) < type ’ numpy . ndarray ’> Un array peut être multidimensionnel (ici dimension 3). >>> a = np . array ( [ [ [1 , 2 ] ,[1 , 2 ] ] ,[ [1 , 2 ] ,[1 , 2 ] ] ] ) >>> a array ( [ [ [1 , 2 ] , [1 , 2 ] ] , [ [1 , 2 ] , [1 , 2 ] ] ] ) >>> type ( a ) < type ’ numpy . ndarray ’> >>> a [1 ,1 , 1 ] 2 On peut manipuler les matrices en travaillant sur les indices (par défaut les indices commencent à 0). Il est important de comprendre la notion d’affectation pour les matrices : les variables a et b regardent – avec des perceptions différentes – une même zone en mémoire. >>> a = np . array ( [1 , 2 , 3 , 4 , 5 ] ) >>> b = a [ 1 : 3 ] >>> b array ( [2 , 3 ] ) >>> b [ 1 ] = 0 25 >>> a # a est modifi é array ( [1 , 2 , 0 , 4 , 5 ] ) La copie, qui doit être explicite, est différente de l’affectation. La mémoire utilisée par b après l’appel à la fonction copy() est séparée de la zone mémoire de a. . >>> a = np . array ( [1 , 2 , 3 , 4 , 5 ] ) >>> b = np . copy ( a [ 1 : 3 ] ) >>> b array ( [2 , 3 ] ) >>> b [ 1 ] = 0 >>> a array ( [1 , 2 , 3 , 4 , 5 ] ) >>> b array ( [2 , 0 ] ) Pour la copie, nous avons utilisé une fonction Numpy. Il est possible de faire autrement car un array est une structure bien plus complexe que de simples listes. Un array possède en réalité une structure objet (cf. chapitre 5). En plus des données, à chaque array est associé un ensemble de fonctions (on parlera alors de méthodes) qu’il est possible d’appeler. >>> a = np . array ( [1 , 2 , 3 , 4 , 5 ] ) >>> b = a [ 1 : 3 ] . copy () >>> b [ 1 ] = 0 >>> b array ( [2 , 0 ] ) >>> a array ( [1 , 2 , 3 , 4 , 5 ] ) Manipulation sur les indices et fonction arange() : >>> a = np . arange ( 10 ) >>> a array ( [0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ] ) >>> a [ 2 : 9 : 3 ] # [ d é but : fin : pas ] array ( [2 , 5 , 8 ] ) >>> a [ 2 : 8 : 3 ] # le dernier é l é ment n ’ est pas inclus array ( [2 , 5 ] ) >>> a [ : 5 ] # le dernier é l é ment n ’ est pas inclus array ( [0 , 1 , 2 , 3 , 4 ] ) >>> a [ 8 : 2 : - 3 ] # les pas n é gatifs sont autoris é s ; l ’ ordre du tableau est invers é array ( [8 , 5 ] ) Un pas négatif inversera l’ordre du tableau. Un indice négatif est considéré comme le nombre d’éléments précédant le dernier élément du tableau. Ainsi a[-2::1] comme a[-2:] extraient l’avant-dernier et le dernier élément. Pour un tableau bi-dimensionnel, on peut bien sûr jouer avec les deux indices. La fonction eye() permet de créer des matrices avec des 1 sur la diagonale et des 0 ailleurs : >>> a = np . eye (5 , 5 ) >>> print a [[ 1. 0. 0. 0. [ 0. 1. 0. 0. [ 0. 0. 1. 0. [ 0. 0. 0. 1. [ 0. 0. 0. 0. >>> a [ :3 , : 4 ] = 4 0.] 0.] 0.] 0.] 1.]] 26 >>> a [ : :3 , : : 4 ] = 5 >>> a [ : ,2 ] = 6 >>> print a [[ 5. 4. 6. 4. [ 4. 4. 6. 4. [ 4. 4. 6. 4. [ 5. 0. 6. 1. [ 0. 0. 6. 0. 5.] 0.] 0.] 5.] 1.]] Les indices peuvent aussi être des listes. La fonction reshape() permet de modifier la taille des matrices : >>> a = np . array ( [2 ,4 ,6 , 8 ] , float ) >>> b = np . array ( [0 ,0 ,1 ,3 ,2 , 1 ] , int ) >>> a [ b ] array ( [ 2 . , 2 . , 4 . , 8 . , 6 . , 4 . ] ) >>> a = a . reshape (2 , 2 ) >>> a array ( [ [ 2 . , 4 . ] , [ 6., 8.]]) >>> b = np . array ( [0 ,0 ,1 ,1 , 0 ] , int ) >>> c = np . array ( [0 ,1 ,1 ,1 , 1 ] , int ) >>> a [b , c ] array ( [ 2 . , 4 . , 8 . , 8 . , 4 . ] ) On peut aussi convertir une matrice de type array en type matrix : >>> a = np . array ( [1 , 2 , 4 ] ) >>> np . mat ( a ) matrix ( [ [1 , 2 , 4 ] ] ) 2.4.2 Construction des matrices Construction de matrices avec la fonction arange() : >>> np . arange ( 10 ) # entiers ( < 10 ) en partant de 0 array ( [0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ] ) >>> np . arange (2 , 10 , dtype = np . float ) # flottants ( < 10 ) dont le premier est 2 . array ( [ 2 . , 3 . , 4 . , 5 . , 6 . , 7 . , 8 . , 9 . ] ) >>> np . arange (2 , 3 , 0 . 1 ) # pas non entier array ( [ 2 . , 2 .1 , 2 .2 , 2 .3 , 2 .4 , 2 .5 , 2 .6 , 2 .7 , 2 .8 , 2 . 9 ] ) >>> np . arange ( 3 ) array ( [0 , 1 , 2 ] ) >>> np . arange ( 3 . 0 ) array ( [ 0 . , 1 . , 2 . ] ) Construction de matrices avec la fonction linspace() : >>> np . linspace ( 1 . , 4 . , 6 ) # 6 flottants r é guli è rement r é partis de 1 . à 4 . ( inclus ) array ( [ 1 . , 1 .6 , 2 .2 , 2 .8 , 3 .4 , 4 . ] ) Utilisation de la fonction reshape() (Attention à respecter le nombre d’éléments) : >>> a = np . arange ( 16 ) >>> a array ( [ 0 , 1 , 2 , 3 , >>> a . reshape (4 , 4 ) array ( [ [ 0 , 1 , 2 , 4, 5, 6, 7, 8, , 15 ] ) 3], 27 9 , 10 , 11 , 12 , 13 , 14 [ 4, 5, 6, 7], [ 8 , 9 , 10 , 11 ] , [ 12 , 13 , 14 , 15 ] ] ) >>> a . reshape (2 , 8 ) array ( [ [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] , [ 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 ] ] ) >>> a . reshape ( -1 , 3 ) Traceback ( most recent call last ) : File " < stdin > " , line 1 , in < module > ValueError : total size of new array must be unchanged >>> a . reshape ( -1 , 2 ) array ( [ [ 0 , 1 ] , [ 2, 3], [ 4, 5], [ 6, 7], [ 8, 9], [ 10 , 11 ] , [ 12 , 13 ] , [ 14 , 15 ] ] ) >>> a . reshape (2 , -1 , 4 ) array ( [ [ [ 0 , 1 , 2 , 3 ] , [ 4, 5, 6, 7]], [ [ 8 , 9 , 10 , 11 ] , [ 12 , 13 , 14 , 15 ] ] ] ) Il est possible de remplacer une dimension par -1 pour compléter avec de quoi stocker toutes le données. Il faut toutefois que le produit des dimensions imposées divise le nombre total d’éléments. a.reshape(-1, 1) organise les données en un vecteur colonne, et a.reshape(1,-1) en un vecteur ligne. Il existe aussi la fonction flatten() transforme une matrice en un vecteur, et crée ce vecteur (on fait une copie de l’objet). La méthode ravel() est équivalente à un reshape vers un vecteur monodimensionnel : a.reshape(-1) ou a.ravel(). Il est possible de concaténer avec la fonction concatenate() des matrices verticalement ou horizontale selon la valeur de axis. >>> a = np . arange ( 4 ) . reshape (2 , 2 ) >>> b = 4 + np . arange ( 4 ) . reshape (2 , 2 ) >>> b array ( [ [4 , 5 ] , [6 , 7 ] ] ) >>> c = ( np . arange ( 4 ) + 6 ) [ newaxis ,~ : ] >>> np . concatenate (( a , b ) ) array ( [ [0 , 1 ] , [2 , 3 ] , [4 , 5 ] , [6 , 7 ] ] ) >>> np . concatenate (( a , b ) , axis = 0 ) array ( [ [0 , 1 ] , [2 , 3 ] , [4 , 5 ] , [6 , 7 ] ] ) >>> np . concatenate (( a , b ) , axis = 1 ) array ( [ [0 , 1 , 4 , 5 ] , [2 , 3 , 6 , 7 ] ] ) >>> np . concatenate (( c ,c , np . concatenate (( a , b ) , axis = 1 ) ) ) array ( [ [6 , 7 , 8 , 9 ] , [6 , 7 , 8 , 9 ] , [0 , 1 , 4 , 5 ] , [2 , 3 , 6 , 7 ] ] ) 28 La fonction arange() génére un vecteur (dimension 1) qui est différent d’une matrice à une ligne ou une colonne (dimension 2). Pour transformer un vecteur en matrice, il faut ajouter une dimension avec np.newaxis : >>> c = ( np . arange ( 4 ) + 6 ) >>> c array ( [6 , 7 , 8 , 9 ] ) >>> c = ( np . arange ( 4 ) + 6 ) [ np . newaxis ,~ : ] >>> c array ( [ [6 , 7 , 8 , 9 ] ] ) Pour travailler sur les indices, il existe les fonctions take() et sa réciproque put() : >>> a = np . arange ( 16 ) . reshape (4 , 4 ) >>> a . take ( [0 , 3 ] , axis = 1 ) array ( [ [ 0 , 3 ] , [ 4, 7], [ 8 , 11 ] , [ 12 , 15 ] ] ) >>> a . take ( [0 , 3 ] ) array ( [0 , 3 ] ) >>> a . take ( [0 , 3 ] , axis = 0 ) array ( [ [ 0 , 1 , 2 , 3 ] , [ 12 , 13 , 14 , 15 ] ] ) >>> a . take ( [0 ,3 ,2 ,2 , 2 ] , axis = 0 ) array ( [ [ 0 , 1 , 2 , 3 ] , [ 12 , 13 , 14 , 15 ] , [ 8 , 9 , 10 , 11 ] , [ 8 , 9 , 10 , 11 ] , [ 8 , 9 , 10 , 11 ] ] ) >>> a = np . array ( [0 , 1 , 2 , 3 , 4 , 5 ] , float ) >>> b = np . array ( [9 ,8 , 7 ] , float ) >>> a . put ( [0 , 3 ] ,b ) >>> a array ( [ 9 . , 1 . , 2 . , 8 . , 4 . , 5 . ] ) 2.4.3 Matrices particulières zeros(n), zeros((n,p)) ones(n), ones((n,p)) empty((n,p)) eye(n), eye(n,p) diag(v) diag(v,k) Vecteur ou matrice de 0 Vecteur ou matrice de 1 Matrice à contenu non initialisé Diagonale de 1 et 0 ailleurs Matrice dont la diagonale est le vecteur v Matrice diag(v) mais diagonale décalée de k La fonction empty ne garantit pas le contenu du tableau : on retrouve le contenu de la mémoire utilisée. Il convient de l’utiliser s’il est certain que toutes cases du tableau seront écrites avant d’être lues. >>> np . empty (( 2 , 3 ) ) array ( [ [ 9 . 04197745e - 266 , [ - 2 . 39052760e - 043 , 2 . 19578687e - 314 , 6 . 35862486e - 321 , ) >>> np . diag ( np . ones ( 3 ) ,1 ) array ( [ [ 0 . , 1 . , 0 . , 0 . ] , 29 4 . 25943278e - 265 ] , 7 . 00258611e - 313 ] ] [ 0., [ 0., [ 0., 0., 0., 0., 1., 0., 0., 0.], 1.], 0.]]) >>> v = np . ones ( 5 ) >>> v array ( [ 1 . , 1 . , 1 . , 1 . , 1 . ] ) >>> np . diag (v , 2 ) array ( [ [ 0 . , 0 . , 1 . , 0 . , 0 . , [ 0., 0., 0., 1., 0., [ 0., 0., 0., 0., 1., [ 0., 0., 0., 0., 0., [ 0., 0., 0., 0., 0., [ 0., 0., 0., 0., 0., [ 0., 0., 0., 0., 0., >>> np . diag (v , - 1 ) array ( [ [ 0 . , 0 . , 0 . , 0 . , 0 . , [ 1., 0., 0., 0., 0., [ 0., 1., 0., 0., 0., [ 0., 0., 1., 0., 0., [ 0., 0., 0., 1., 0., [ 0., 0., 0., 0., 1., 0., 0., 0., 1., 0., 0., 0., 0.], 0.], 0.], 0.], 1.], 0.], 0.]]) 0.], 0.], 0.], 0.], 0.], 0.]]) On peut aussi générer des vecteurs ou des matrices aléatoires (distribution uniforme) de nombres entre 0 et 1 avec la fonction rand(). Mais cette fonction appartient à une sous bibliothèque random, que l’on peut appeler comme cela : >>> np . random . rand ( 4 ) array ( [ 0 . 67059949 , 0 . 17947019 , 0 . 7217041 , 0 . 7823382 ] ) >>> np . random . rand (4 , 2 ) array ( [ [ 0 . 17309785 , 0 . 82626591 ] , [ 0 . 7911254 , 0 . 19542556 ] , [ 0 . 45654595 , 0 . 92389837 ] , [ 0 . 74930928 , 0 . 40855282 ] ] ) >>> from numpy . random import rand # alternative pour importer la fonction rand >>> rand ( 2 ) array ( [ 0 . 22220557 , 0 . 02390764 ] ) >>> rand (2 , 3 ) array ( [ [ 0 . 17719008 , 0 . 78686815 , 0 . 89788023 ] , [ 0 . 00363013 , 0 . 2147351 , 0 . 47286765 ] ] ) 2.4.4 Opérations On peut additionner deux vecteurs ou deux matrices de même dimensions avec l’opérateur +. On peut aussi additionner ou multiplier un scalaire par un vecteur ou une matrice par exemple 2.+a ou 2.*a. De même a**3 passe chaque élément de a au cube et 1/a calcule l’inverse de chaque élément de a : >>> a = np . random . rand (2 , 2 ) + np . ones (( 2 , 2 ) ) >>> a array ( [ [ 1 . 59691955 , 1 . 2573431 ] , [ 1 . 17175816 , 1 . 70118795 ] ] ) >>> 1 / a array ( [ [ 0 . 62620562 , 0 . 79532786 ] , [ 0 . 85341842 , 0 . 58782453 ] ] ) L’opérateur * entre deux vecteurs ou deux matrices de même dimension effectue une multiplication terme à terme. Entre une matrice et un vecteur de dimension compatible, l’opérateur * effectue une multiplication terme à terme 30 sur chaque ligne et non une multiplication algébrique (contrairement au type matrix) : >>> a = 3 . * np . ones (( 2 , 2 ) ) >>> a * a array ( [ [ 9 . , 9 . ] , [ 9., 9.]]) >>> v = np . arange (1 , 3 ) >>> v array ( [1 , 2 ] ) >>> a * v array ( [ [ 3 . , 6 . ] , [ 3., 6.]]) Le produit algébrique se réalise avec la fonction dot() : >>> a = np . arange ( 4 ) . reshape (2 , 2 ) >>> v = np . array ( [ -3 , 2 ] ) >>> np . dot (a , v ) array ( [2 , 0 ] ) >>> np . dot (v , a ) array ( [4 , 3 ] ) >>> w = np . concatenate (( v , v ) ) >>> w array ( [ -3 , 2 , -3 , 2 ] ) >>> dot (a , w ) Traceback ( most recent call last ) ~ : File " < stdin > " , line 1 , in < module > ValueError ~ : objects are not aligned On peut aussi réaliser le produit scalaire de 2 vecteurs avec la fonction vdot(). Il existe aussi le produit de Kronecker avec kron() : >>> a = np . arange (4 , dtype = float ) . reshape (2 , 2 ) >>> b = np . arange (5 , dtype = float ) >>> print a , b [[ 0. 1.] [ 2. 3.]] [ 0. 1. 2. 3. 4.] >>> np . kron (a , b ) array ( [ [ 0 . , 0., 0., 0., 0., 0., .], [ 0., 2., 4., 6., 8., 0., .]]) >>> np . kron (b , a ) array ( [ [ 0 . , 0., 0., 1., 0., 2., .], [ 0., 0., 2., 3., 4., 6., .]]) >>> a = np . arange (5 , dtype = float ) >>> b = np . ones (( 1 , 5 ) ) >>> np . kron (a , b . transpose () ) array ( [ [ 0 . , 1 . , 2 . , 3 . , 4 . ] , [ 0., 1., 2., 3., 4.], [ 0., 1., 2., 3., 4.], [ 0., 1., 2., 3., 4.], [ 0., 1., 2., 3., 4.]]) 1., 2., 3., 4 3., 6., 9., 12 0., 3., 0., 4 6., 9., 8., 12 La fonction rank() calcule le rang d’une matrice. 2.4.5 Sous-bibliothèque linalg La sous-bilbiothèque linalg permet l’inversion (inv()), la résolution de systèmes linéaires (solve()), le calcul du déterminant (det()), le calcul des 31 vecteurs et valeurs propres (eig()), ou sa décomposition en valeur singulières (svd()) : >>> a = np . array ( [2 ,4 ,6 , 8 ] , float ) . reshape (2 , 2 ) >>> np . linalg . inv ( a ) array ( [ [ - 1 . , 0 . 5 ] , [ 0 . 75 , - 0 . 25 ] ] ) >>> a = np . array ( [2 ,4 ,6 , 8 ] , float ) . reshape (2 , 2 ) >>> b = np . array ( [1 , 4 ] , float ) >>> np . linalg . solve (a , b ) array ( [ 1 . , - 0 . 25 ] ) >>> b = np . array ( [ [1 , 1 ] ,[4 , - 4 ] ] , float ) >>> np . linalg . solve (a , b ) array ( [ [ 1 . , - 3 . ] , [ - 0 . 25 , 1 . 75 ] ] ) >>> a = np . array ( [2 ,4 ,6 , 8 ] , float ) . reshape (2 , 2 ) >>> np . linalg . eig ( a ) ( array ( [ - 0 . 74456265 , 10 . 74456265 ] ) , array ( [ [ - 0 . 82456484 , - 0 . 41597356 ] , [ 0 . 56576746 , - 0 . 90937671 ] ] ) ) >>> (U , S , V ) = np . linalg . svd ( a ) >>> print " U : " ,U , " \ nS : " , S , " \ nV : " , V U : [ [ - 0 . 40455358 - 0 . 9145143 ] [ - 0 . 9145143 0 . 40455358 ] ] S : [ 10 . 92997141 0 . 73193238 ] V : [ [ - 0 . 57604844 - 0 . 81741556 ] [ 0 . 81741556 - 0 . 57604844 ] ] 2.4.6 Fonctions numériques de Numpy sur les matrices Toutes les fonction numériques de numpy s’appliquent aussi à des vecteurs ou des matrices élément par élément : >>> a = np . arange ( 5 ) >>> a array ( [0 , 1 , 2 , 3 , 4 ] ) >>> np . square ( a ) array ( [ 0 , 1 , 4 , 9 , 16 ] ) 2.4.7 La bibliothèque Scipy La bibliothèque numpy ne contient pas toutes les fonctions envisageables. Pour la compléter, il existe la bibliothèque scipy qui s’appuie sur numpy et qui contient des fonctions de plus haut niveau. Par exemple, la fonction numérique exp() appliquée à une matrice travaille élément par élément. Pour calculer l’exponentielle d’une matrice, il faut utiliser les fonctions expm() (approximation de Padé), expm2() (diagonalisation) ou expm3() (série de taylor) contenues dans la sous-bibliothèque linalg de scipy. >>> a = np . arange ( 16 ) >>> a array ( [ 0 , 1 , 2 , 3 , 4, 5, 6, 7, 8, , 15 ] ) >>> a . reshape (( 4 , 4 ) ) array ( [ [ 0 , 1 , 2 , 3 ] , 32 9 , 10 , 11 , 12 , 13 , 14 [ 4, 5, 6, 7], [ 8 , 9 , 10 , 11 ] , [ 12 , 13 , 14 , 15 ] ] ) >>> a array ( [ 0 , 1, 2, 3, 4, 5, 6, 7, 8, , 15 ] ) 9 , 10 , 11 , 12 , 13 , 14 >>> a = a . reshape (( 4 , 4 ) ) >>> np . exp ( a ) array ( [ [ 1 . 00000000e + 00 , 2 . 71828183e + 00 , 7 . 38905610e + 00 , 2 . 00855369e + 01 ] , [ 5 . 45981500e + 01 , 1 . 48413159e + 02 , 4 . 03428793e + 02 , 1 . 09663316e + 03 ] , [ 2 . 98095799e + 03 , 8 . 10308393e + 03 , 2 . 20264658e + 04 , 5 . 98741417e + 04 ] , [ 1 . 62754791e + 05 , 4 . 42413392e + 05 , 1 . 20260428e + 06 , 3 . 26901737e + 06 ] ] ) >>> import scipy . linalg as spl # importation de la sous biblioth è que linalg de Scipy >>> spl . expm ( a ) array ( [ [ 6 . 20364637e + 12 , 7 . 14131081e + 12 , 8 . 07897526e + 12 , 9 . 01663970e + 12 ] , [ 1 . 79304209e + 13 , 2 . 06405557e + 13 , 2 . 33506906e + 13 , 2 . 60608254e + 13 ] , [ 2 . 96571954e + 13 , 3 . 41398007e + 13 , 3 . 86224059e + 13 , 4 . 31050111e + 13 ] , [ 4 . 13839700e + 13 , 4 . 76390456e + 13 , 5 . 38941212e + 13 , 6 . 01491968e + 13 ] ] ) La bibliothèque scipy propose également des fonctions de quadrature numérique, d’intégration numérique d’équations différentielles, de résolution numérique de systèmes d’équation, d’optimisation numérique, . . . (cf. section 3.7) 2.5 Tracer des courbes Il existe différentes bibliothèques qui permettent de tracer des courbes. Ces bibliothèques sont plus ou moins complètes, se caractérisent par un certain niveau de facilité à la mise en œuvre et ont une qualité graphique donnée. Parmi toutes celles possibles, la bibliothèque MatPlotLib présente un excellent compromis et est d’usage extrêmement fréquent. Sur le web, il est très simple de trouver des présentations de l’utilisation de la bibliothèque MatPlotLib avec les codes en open source. Ici, cette section reprend en grande partie la présentation faite par Nicolas P. Rougier sur son site [Rou12] 2.5.1 Première approche Voici un premier exemple très simple utilisant la sous-bibliothèque pyplot qui permet de tracer une courbe et ouvre une fenêtre graphique avec un certain nombre de fonctionnalités (à tester) : 1 2 3 4 5 6 # -*- coding: utf-8 -*import numpy as np import matplotlib.pyplot as plt from matplotlib.backends.backend_pdf import PdfPages # Pour sauvegarder en pdf plt.plot([1,2,3,4]) plt.xlabel(’Abscisses’) 33 7 plt.ylabel(unicode(’Ordonnées’, ’utf-8’)) 8 9 10 # Pour sauvegarder la figure sous forme d’image #plt.savefig("../img/mpl_first.png",dpi=72) 11 12 13 14 15 # Pour sauvegarder la figure en pdf pp = PdfPages("../img/mpl_first.pdf") pp.savefig() pp.close() 16 17 plt.show() 4.0 3.5 Ordonnées 3.0 2.5 2.0 1.5 1.00.0 0.5 1.0 1.5 Abscisses 2.0 2.5 3.0 Une autre possibilité pour obtenir un résultat équivalent est d’utiliser la sous-bibliothèque pylab. C’est la solution que nous adopterons. Les fonctions de pylab ressemblent beaucoup aux fonctions graphiques d’autres langages comme Matlab ou Scilab. Nous privilégierons cette solution dans la suite. 1 2 3 4 5 # -*- coding: utf-8 -*import numpy as np from pylab import * # Pour sauvegarder en pdf : from matplotlib.backends.backend_pdf import PdfPages 6 7 8 9 10 11 """ essai""" n = 256 X = np.linspace(-np.pi, np.pi, 256,endpoint=True) C,S = np.cos(X), np.sin(X) plot(X,C),plot(X,S) 12 13 14 # Pour sauvegarder la figure sous forme d’image # plt.savefig("../img/mpl_basic.png",dpi=72) 15 16 17 18 19 # Pour sauvegarder la figure en pdf pp = PdfPages("../img/mpl_basic.pdf") pp.savefig() pp.close() 20 21 show() 34 1.0 0.5 0.0 0.5 1.0 4 3 2 1 0 1 2 3 4 Une version équivalente du programme précédent, mais avec plus de détails : 1 2 3 4 # -*- coding: utf-8 -*import numpy as np from pylab import * from matplotlib.backends.backend_pdf import PdfPages 5 6 7 # Create a new figure of size 8x6 inches, using 80 dots per inch figure(figsize=(8,6), dpi=80) 8 9 10 # Create a new subplot from a grid of 1x1 subplot(111) 11 12 13 X = np.linspace(-np.pi, np.pi, 256,endpoint=True) C,S = np.cos(X), np.sin(X) 14 15 16 # Plot cosine using blue color with a continuous line of width 1 (point) plot(X, C, color="blue", linewidth=1.0, linestyle="-") 17 18 19 # Plot sine using green color with a continuous line of width 1 (point) plot(X, S, color="green", linewidth=1.0, linestyle="-") 20 21 22 # Set x limits xlim(-4.0,4.0) 23 24 25 # Set x ticks xticks(np.linspace(-4,4,9,endpoint=True)) 26 27 28 # Set y limits ylim(-1.0,1.0) 29 30 31 # Set y ticks yticks(np.linspace(-1,1,5,endpoint=True)) 32 33 34 # To save into a png picture #plt.savefig("../img/mpl_basic2.png",dpi=72) 35 36 37 # Pour sauvegarder la figure en pdf pp = PdfPages("../img/mpl_basic2.pdf") 35 38 39 pp.savefig() pp.close() 40 41 42 # Show result on screen show() On peut modifier la couleur, le style et l’épaisseur des courbes. À noter qu’un pouce inch mesure 2.54cm et qu’un point typographique mesure 1/72 de pouce soit un peu plus d’un tiers de millimètre. 1 2 3 4 # -*- coding: utf-8 -*import numpy as np from pylab import * from matplotlib.backends.backend_pdf import PdfPages 5 6 7 figure(figsize=(8,5), dpi=80) subplot(111) 8 9 10 X = np.linspace(-np.pi, np.pi, 256,endpoint=True) C,S = np.cos(X), np.sin(X) 11 12 13 plot(X, C, color="blue", linewidth=2.5, linestyle="-") plot(X, S, color="red", linewidth=2.5, linestyle="--") 14 15 16 xlim(-4.0,4.0) xticks(np.linspace(-4,4,9,endpoint=True)) 17 18 19 ylim(-1.0,1.0) yticks(np.linspace(-1,1,5,endpoint=True)) 20 21 22 # Pour sauvegarder la figure sous forme d’image #plt.savefig("../img/mpl_basic3.png",dpi=72) 23 24 25 26 27 # Pour sauvegarder la figure en pdf pp = PdfPages("../img/mpl_basic3.pdf") pp.savefig() pp.close() 28 29 show() 1.0 0.5 0.0 0.5 1.0 4 3 2 1 0 1 On peut modifier les limites des abscisses : 1 # -*- coding: utf-8 -*-import numpy as np 36 2 3 4 2 3 4 import numpy as np from pylab import * from matplotlib.backends.backend_pdf import PdfPages 5 6 7 figure(figsize=(8,5), dpi=80) subplot(111) 8 9 10 X = np.linspace(-np.pi, np.pi, 256,endpoint=True) C,S = np.cos(X), np.sin(X) 11 12 13 plot(X, C, color="blue", linewidth=2.5, linestyle="-") plot(X, S, color="red", linewidth=2.5, linestyle="--") 14 15 16 xlim(X.min()*1.1, X.max()*1.1) ylim(C.min()*1.1,C.max()*1.1) 17 18 19 # Pour sauvegarder la figure sous forme d’image #plt.savefig("../img/mpl_basic4.png",dpi=72) 20 21 22 23 24 # Pour sauvegarder la figure en pdf pp = PdfPages("../img/mpl_basic4.pdf") pp.savefig() pp.close() 25 26 show() 1.0 0.5 0.0 0.5 1.0 3 2 1 0 1 2 On peut modifier le pas des abscisses : 1 2 3 4 # -*- coding: utf-8 -*import numpy as np from pylab import * from matplotlib.backends.backend_pdf import PdfPages 5 6 7 figure(figsize=(8,5), dpi=80) subplot(111) 8 9 10 X = np.linspace(-np.pi, np.pi, 256,endpoint=True) C,S = np.cos(X), np.sin(X) 11 12 13 plot(X, C, color="blue", linewidth=2.5, linestyle="-") plot(X, S, color="red", linewidth=2.5, linestyle="--") 14 15 xlim(X.min()*1.1, X.max()*1.1) 37 3 16 xticks([-np.pi, -np.pi/2, 0, np.pi/2, np.pi]) 17 18 19 ylim(C.min()*1.1,C.max()*1.1) yticks([-1, 0, +1]) 20 21 22 # Pour sauvegarder la figure sous forme d’image #plt.savefig("../img/mpl_basic5.png",dpi=72) 23 24 25 26 27 # Pour sauvegarder la figure en pdf pp = PdfPages("../img/mpl_basic5.pdf") pp.savefig() pp.close() 28 29 show() 1 0 1 3.14159265 1.57079633 0.00000000 38 1.57079633 3.14159265 Les abscisses avec du texte Latex : 1 2 3 4 # -*- coding: utf-8 -*import numpy as np from pylab import * from matplotlib.backends.backend_pdf import PdfPages 5 6 7 figure(figsize=(8,5), dpi=80) subplot(111) 8 9 10 X = np.linspace(-np.pi, np.pi, 256,endpoint=True) C,S = np.cos(X), np.sin(X) 11 12 13 plot(X, C, color="blue", linewidth=2.5, linestyle="-") plot(X, S, color="red", linewidth=2.5, linestyle="--") 14 15 16 17 xlim(X.min()*1.1, X.max()*1.1) xticks([-np.pi, -np.pi/2, 0, np.pi/2, np.pi], [r’$-\pi$’, r’$-\pi/2$’, r’$0$’, r’$+\pi/2$’, r’$+\pi$’]) 18 19 20 21 ylim(C.min()*1.1,C.max()*1.1) yticks([-1, 0, +1], [r’$-1$’, r’$0$’, r’$+1$’]) 22 23 24 # Pour sauvegarder la figure sous forme d’image #plt.savefig("../img/mpl_basic6.png",dpi=72) 25 26 27 28 29 # Pour sauvegarder la figure en pdf pp = PdfPages("../img/mpl_basic6.pdf") pp.savefig() pp.close() 30 31 show() +1 0 −1 −π −π/2 0 39 + π/2 +π On peut modifier le cadre et la position des axes : 1 2 3 4 # -*- coding: utf-8 -*import numpy as np from pylab import * from matplotlib.backends.backend_pdf import PdfPages 5 6 7 figure(figsize=(8,5), dpi=80) subplot(111) 8 9 10 X = np.linspace(-np.pi, np.pi, 256,endpoint=True) C,S = np.cos(X), np.sin(X) 11 12 13 plot(X, C, color="blue", linewidth=2.5, linestyle="-") plot(X, S, color="red", linewidth=2.5, linestyle="--") 14 15 16 17 18 19 20 21 ax = gca() ax.spines[’right’].set_color(’none’) ax.spines[’top’].set_color(’none’) ax.xaxis.set_ticks_position(’bottom’) ax.spines[’bottom’].set_position((’data’,0)) ax.yaxis.set_ticks_position(’left’) ax.spines[’left’].set_position((’data’,0)) 22 23 24 25 xlim(X.min()*1.1, X.max()*1.1) xticks([-np.pi, -np.pi/2, 0, np.pi/2, np.pi], [r’$-\pi$’, r’$-\pi/2$’, r’$0$’, r’$+\pi/2$’, r’$+\pi$’]) 26 27 28 29 ylim(C.min()*1.1,C.max()*1.1) yticks([-1, 0, +1], [r’$-1$’, r’$0$’, r’$+1$’]) 30 31 32 # Pour sauvegarder la figure sous forme d’image #plt.savefig("../img/mpl_basic7.png",dpi=72) 33 34 35 36 37 # Pour sauvegarder la figure en pdf pp = PdfPages("../img/mpl_basic7.pdf") pp.savefig() pp.close() 38 39 show() +1 −π −π/2 0 0 −1 40 + π/2 +π Ajouter une légende : 1 2 3 4 # -*- coding: utf-8 -*import numpy as np from pylab import * from matplotlib.backends.backend_pdf import PdfPages 5 6 7 figure(figsize=(8,5), dpi=80) subplot(111) 8 9 10 X = np.linspace(-np.pi, np.pi, 256,endpoint=True) C,S = np.cos(X), np.sin(X) 11 12 13 plot(X, C, color="blue", linewidth=2.5, linestyle="-", label="cosine") plot(X, S, color="red", linewidth=2.5, linestyle="--", label="sine") 14 15 16 17 18 19 20 21 ax = gca() ax.spines[’right’].set_color(’none’) ax.spines[’top’].set_color(’none’) ax.xaxis.set_ticks_position(’bottom’) ax.spines[’bottom’].set_position((’data’,0)) ax.yaxis.set_ticks_position(’left’) ax.spines[’left’].set_position((’data’,0)) 22 23 24 25 xlim(X.min()*1.1, X.max()*1.1) xticks([-np.pi, -np.pi/2, 0, np.pi/2, np.pi], [r’$-\pi$’, r’$-\pi/2$’, r’$0$’, r’$+\pi/2$’, r’$+\pi$’]) 26 27 28 29 ylim(C.min()*1.1,C.max()*1.1) yticks([-1, +1], [r’$-1$’, r’$+1$’]) 30 31 32 legend(loc=’upper left’) 33 34 35 # Pour sauvegarder la figure sous forme d’image #plt.savefig("../img/mpl_basic8.png",dpi=72) 36 37 38 39 40 # Pour sauvegarder la figure en pdf pp = PdfPages("../img/mpl_basic8.pdf") pp.savefig() pp.close() 41 42 show() 41 cosine sine −π +1 −π/2 0 + π/2 +π −1 Annoter des points : 1 2 3 4 # -*- coding: utf-8 -*import numpy as np from pylab import * from matplotlib.backends.backend_pdf import PdfPages 5 6 7 figure(figsize=(8,5), dpi=80) subplot(111) 8 9 10 X = np.linspace(-np.pi, np.pi, 256,endpoint=True) C,S = np.cos(X), np.sin(X) 11 12 13 plot(X, C, color="blue", linewidth=2.5, linestyle="-", label="cosine") plot(X, S, color="red", linewidth=2.5, linestyle="--", label="sine") 14 15 16 17 18 19 20 21 ax = gca() ax.spines[’right’].set_color(’none’) ax.spines[’top’].set_color(’none’) ax.xaxis.set_ticks_position(’bottom’) ax.spines[’bottom’].set_position((’data’,0)) ax.yaxis.set_ticks_position(’left’) ax.spines[’left’].set_position((’data’,0)) 22 23 24 25 xlim(X.min()*1.1, X.max()*1.1) xticks([-np.pi, -np.pi/2, 0, np.pi/2, np.pi], [r’$-\pi$’, r’$-\pi/2$’, r’$0$’, r’$+\pi/2$’, r’$+\pi$’]) 26 27 28 29 ylim(C.min()*1.1,C.max()*1.1) yticks([-1, +1], [r’$-1$’, r’$+1$’]) 30 31 32 33 34 35 36 37 t = 2*np.pi/3 plot([t,t],[0,np.cos(t)], color =’blue’, linewidth=1.5, linestyle="--") scatter([t,],[np.cos(t),], 50, color =’blue’) annotate(r’$\cos(\frac{2\pi}{3})=-\frac{1}{2}$’, xy=(t, np.cos(t)), xycoords=’data’, xytext=(-90, -50), textcoords=’offset points’, fontsize=16, arrowprops=dict(arrowstyle="->", connectionstyle="arc3,rad=.2")) 38 39 40 plot([t,t],[0,np.sin(t)], color =’red’, linewidth=1.5, linestyle="--") 42 41 42 43 44 scatter([t,],[np.sin(t),], 50, color =’red’) annotate(r’$\sin(\frac{2\pi}{3})=\frac{\sqrt{3}}{2}$’, xy=(t, np.sin(t)) , xycoords=’data’, xytext=(+10, +30), textcoords=’offset points’, fontsize=16, arrowprops=dict(arrowstyle="->", connectionstyle="arc3,rad=.2")) 45 46 legend(loc=’upper left’) 47 48 49 # Pour sauvegarder la figure sous forme d’image #plt.savefig("../img/mpl_basic9.png",dpi=72) 50 51 52 53 54 # Pour sauvegarder la figure en pdf pp = PdfPages("../img/mpl_basic9.pdf") pp.savefig() pp.close() 55 56 show() cosine sine −π sin(23π ) = +1 −π/2 0 −1 43 + π/2 cos(23π ) = −12 p +π 3 2 Quelques améliorations : 1 2 3 4 # -*- coding: utf-8 -*import numpy as np from pylab import * from matplotlib.backends.backend_pdf import PdfPages 5 6 7 figure(figsize=(8,5), dpi=80) subplot(111) 8 9 10 X = np.linspace(-np.pi, np.pi, 256,endpoint=True) C,S = np.cos(X), np.sin(X) 11 12 13 plot(X, C, color="blue", linewidth=2.5, linestyle="-", label="cosine") plot(X, S, color="red", linewidth=2.5, linestyle="--", label="sine") 14 15 16 17 18 19 20 21 ax = gca() ax.spines[’right’].set_color(’none’) ax.spines[’top’].set_color(’none’) ax.xaxis.set_ticks_position(’bottom’) ax.spines[’bottom’].set_position((’data’,0)) ax.yaxis.set_ticks_position(’left’) ax.spines[’left’].set_position((’data’,0)) 22 23 24 25 xlim(X.min()*1.1, X.max()*1.1) xticks([-np.pi, -np.pi/2, 0, np.pi/2, np.pi], [r’$-\pi$’, r’$-\pi/2$’, r’$0$’, r’$+\pi/2$’, r’$+\pi$’]) 26 27 28 29 ylim(C.min()*1.1,C.max()*1.1) yticks([-1, +1], [r’$-1$’, r’$+1$’]) 30 31 32 legend(loc=’upper left’) 33 34 35 36 37 38 39 40 t = 2*np.pi/3 plot([t,t],[0,np.cos(t)], color =’blue’, linewidth=.5, linestyle="--") scatter([t,],[np.cos(t),], 50, color =’blue’) annotate(r’$\sin\left(\frac{2\pi}{3}\right)=\frac{\sqrt{3}}{2}$’, xy=(t, np.sin(t)), xycoords=’data’, xytext=(+10, +30), textcoords=’offset points’, fontsize=16, arrowprops=dict(arrowstyle="->", connectionstyle="arc3,rad=.2")) 41 42 43 44 45 46 47 plot([t,t],[0,np.sin(t)], color =’red’, linewidth=.5, linestyle="--") scatter([t,],[np.sin(t),], 50, color =’red’) annotate(r’$\cos\left(\frac{2\pi}{3}\right)=-\frac{1}{2}$’, xy=(t, np. cos(t)), xycoords=’data’, xytext=(-90, -50), textcoords=’offset points’, fontsize=16, arrowprops=dict(arrowstyle="->", connectionstyle="arc3,rad=.2")) 48 49 50 51 for label in ax.get_xticklabels() +ax.get_yticklabels(): label.set_fontsize(16) label.set_bbox(dict(facecolor=’white’, edgecolor=’None’, alpha=0.65 )) 52 53 54 55 # Pour sauvegarder la figure sous forme d’image #plt.savefig("../img/mpl_basic10.png",dpi=72) 56 57 58 # Pour sauvegarder la figure en pdf pp = PdfPages("../img/mpl_basic10.pdf") 44 59 60 pp.savefig() pp.close() 61 62 show() +1 cosine sine −π sin −π/2 + π/2 0 cos −1 2.5.2 ³ p 2π ´ = 3 3 2 +π ³ 2π ´ = −1 3 2 Des graphiques plus élaborés Des exemples de graphiques plus sophistiqués sont fournis dans les fichiers “mpl_xxxxx.py” accompagnant cette formation. Par ailleurs, il existe de très nombreux exemples de graphiques réalisés avec MatPlotLib (code fourni) sous le site : matplotlib.org/gallery.html#pylab_examples. Graphique multiple Dans le fichier “mpl_multi_graph.py”, il y un exemple de fenêtre graphique multiple. 45 Titre d'en haut 1.0 0.5 0.0 0.5 Ordonnée 1.00 1.0 Série 1 2 4 6 Titre d'en bas 8 10 Série 2 0.5 0.0 0.5 1.00 1 2 Abscisse 3 4 5 Graphique animé Dans le fichier “mpl_animation.py”, il y un exemple de courbe animée : 1 2 3 4 5 6 7 # -*- coding: utf-8 -*""" A simple example of an animated plot """ import numpy as np import matplotlib.pyplot as plt import matplotlib.animation as animation 8 9 10 fig = plt.figure() ax = fig.add_subplot(111) 11 12 13 x = np.arange(0, 2*np.pi, 0.01) line, = ax.plot(x, np.sin(x)) # x-array 14 15 16 17 def animate(i): line.set_ydata(np.sin(x+i/10.0)) # update the data return line, 18 19 20 21 22 #Init only required for blitting to give a clean slate. def init(): line.set_ydata(np.ma.array(x, mask=True)) return line, 23 24 25 26 ani = animation.FuncAnimation(fig, animate, np.arange(1, 200), init_func =init, interval=25, blit=True) plt.show() 46 Un autre exemple (fichier “mpl_animation2.py”) décrit le mouvement d’un pendule double : 1 2 3 # -*- coding: utf-8 -*# Double pendulum formula translated from the C code at # http://www.physics.usyd.edu.au/~wheat/dpend_html/solve_dpend.c 4 5 6 7 8 9 from numpy import sin, cos, pi, array import numpy as np import matplotlib.pyplot as plt import scipy.integrate as integrate import matplotlib.animation as animation 10 11 12 13 14 15 G = 9.8 L1 = 1.0 L2 = 1.0 M1 = 1.0 M2 = 1.0 # # # # # acceleration due to gravity, in m/s^2 length of pendulum 1 in m length of pendulum 2 in m mass of pendulum 1 in kg mass of pendulum 2 in kg 16 17 18 def derivs(state, t): 19 20 21 dydx = np.zeros_like(state) dydx[0] =state[1] 22 23 24 25 26 27 del_ = state[2]-state[0] den1 = (M1+M2)*L1 -M2*L1*cos(del_)*cos(del_) dydx[1] =(M2*L1*state[1]*state[1]*sin(del_)*cos(del_) + M2*G*sin(state[2])*cos(del_) +M2*L2*state[3]*state[3]* sin(del_) - (M1+M2)*G*sin(state[0]))/den1 28 29 dydx[2] =state[3] 30 31 32 33 34 35 den2 = (L2/L1)*den1 dydx[3] =(-M2*L2*state[3]*state[3]*sin(del_)*cos(del_) + (M1+M2)*G*sin(state[0])*cos(del_) - (M1+M2)*L1*state[1]*state[1]*sin(del_) - (M1+M2)*G*sin(state[2]))/den2 36 37 return dydx 38 39 40 41 # create a time array from 0..100 sampled at 0.1 second steps dt = 0.05 t = np.arange(0.0, 20, dt) 42 43 44 45 46 47 48 # th1 and th2 are the initial angles (degrees) # w10 and w20 are the initial angular velocities (degrees per second) th1 = 120.0 w1 = 0.0 th2 = -10.0 w2 = 0.0 49 50 rad = pi/180 51 52 53 # initial state state = np.array([th1, w1, th2, w2])*pi/180. 54 55 56 # integrate your ODE using scipy.integrate. y = integrate.odeint(derivs, state, t) 57 58 x1 = L1*sin(y[:,0]) 47 59 y1 = -L1*cos(y[:,0]) 60 61 62 x2 = L2*sin(y[:,2]) +x1 y2 = -L2*cos(y[:,2]) +y1 63 64 65 66 fig = plt.figure() ax = fig.add_subplot(111, autoscale_on=False, xlim=(-2, 2), ylim=(-2, 2) ) ax.grid() 67 68 69 70 line, = ax.plot([], [], ’o-’, lw=2) time_template =’time =%.1fs’ time_text =ax.text(0.05, 0.9, ’’, transform=ax.transAxes) 71 72 73 74 75 def init(): line.set_data([], []) time_text.set_text(’’) return line, time_text 76 77 78 79 def animate(i): thisx = [0, x1[i], x2[i]] thisy = [0, y1[i], y2[i]] 80 81 82 83 line.set_data(thisx, thisy) time_text.set_text(time_template%(i*dt)) return line, time_text 84 85 86 ani = animation.FuncAnimation(fig, animate, np.arange(1, len(y)), interval=25, blit=True, init_func=init) 87 88 89 #ani.save(’../img/mpl_animation2.mp4’, fps=15, clear_temp=True) plt.show() Pour la dimension 3 Le fichier “mpl_surf.py” est un exemple de représentation en 3D d’une fonction à deux variables. 2.0 1.5 1.0 0.5 0.0 0.5 1.0 1.5 2.0 4 3 2 1 0 1 2 3 4 3 2 1 0 1 2 3 Le fichier “mpl_contour.py” est un exemple de représentation d’une fonc48 tion à deux variables en niveaux de couleur et en contours. 0.5 0.75 0 0 50 -0.250 0 0.500 0.75 00 0 0.00 -0. 1.000 0.250 49 3 Structures algorithmiques Sommaire 3.1 3.2 3.3 3.4 3.5 3.6 3.7 Types de données dynamiques 3.1.1 Listes 3.1.2 Tuples 3.1.3 Tables de hachage Boucles et conditionnelles 3.2.1 Conditionnelle 3.2.2 Type booléen 3.2.3 Boucles 3.2.4 Boucle définissant une liste Définition de fonction 3.3.1 Variables locales et globales 3.3.2 Valeur(s) de retour 3.3.3 Valeur par défaut des paramètres 3.3.4 Fonctions récursives Lancement d’un programme 3.4.1 Ligne de commande 3.4.2 Récupérer les arguments de la ligne de commande 3.4.3 Type string 3.4.4 Opérateur % 3.4.5 Affichage Algorithmes de tri 3.5.1 Tri par sélection 3.5.2 Tri bulles 3.5.3 Tri par insertion 3.5.4 Tri shell 3.5.5 Tri par segmentation, ou Quicksort 3.5.6 Méthode sort des listes de Python 3.5.7 Complexité des algorithmes de tris Modules Simulations numériques 3.7.1 Tirages aléatoires et statistiques 3.7.2 Résolution d’équation(s) 3.7.3 Quadrature numérique 3.7.4 Intégration numérique des ODE C 51 51 52 52 53 53 54 54 55 55 55 56 57 57 58 58 58 59 60 60 60 60 62 63 65 66 67 67 68 69 69 70 71 71 omme tout langage informatique, Python est Turing-complet. Contrairement à beaucoup, le code est pauvre en signes de ponctuation et toujours bien indenté, ce qui le rend agréable à lire. 50 Les mots-clefs et les identifiants sont tous sensibles à la casse des caractères : foo, Foo, fOO et FOO sont des variables différentes. Les caractères suivant un # sont des commentaires, destinés aux autres lecteurs du code source. La première ligne # -*- coding: utf-8 -*- est particulière : elle indique à la machine virtuelle Python que le code source est écrit en UTF-8, ce qui permet les caractères accentués. 3.1 3.1.1 Types de données dynamiques Listes Une liste permet de stocker des successions de valeurs, et de les retrouver par leur index dans la structure. Elle peut être modifiée : modification des éléments présents, suppression, et ajout d’élément(s). Une list s’écrit entre crochets : tab = [6, 7]. La numérotation des cases commence à 0. L’index se place entre crochets : tab[4]. Il est possible de sélectionner des tranches de ces structures : tab[1:4], tab[:5], tab[3:], tab[1:-1]. Le premier élément est inclus, le dernier est exclu : tab[1:3] contient les cases 1 et 2. L’élément d’indice -1 est le dernier élément de la liste, l’élément d’indice -2 le pénultième, etc. Il n’est pas possible de créer une nouvelle case à une liste par une simple affectation. Si tab est de taille 8, tab[8]=42 entraîne une erreur. Il faut ajouter la nouvelle valeur par tab.append(42). On peut concaténer liste2 à liste1 par liste1.extend(liste2). L’opérateur len() donne le nombre d’éléments de la liste : len([3, 7, [5, 3], "Last"]) renvoie 4. On peut supprimer un élément d’une liste en connaissant son index i par del tab[i]. On peut supprimer le premier élément ayant une certaine valeur val par tab.remove(val). Attention : tab.remove(tab[i]) ne supprime donc pas forcément l’élément d’index i. La méthode pop() supprime et renvoie le dernier élément d’une liste : liste = [9 , 6 , 42 ] x = liste . pop () La variable x vaut désormais 42, et liste vaut [9, 6]. La méthode insert(i, x) insère l’élément x à l’indice i. Si i est supérieur ou égal à la taille de la liste, x est inséré en dernière position, comme un appel à append(x). liste = [9 , 6 , 42 ] liste . insert (1 , 7 ) liste . insert ( 100 , 24 ) La variable liste vaut désormais [9, 7, 6, 42, 24]. On peut tester l’appartenance d’un élément à une liste par l’opérateur in : primes = [2 , 3 , 5 , 7 , 11 , 13 , 17 , 19 ] if 5 in primes : print " % i is a prime number " % ( 5 ) 51 Un tableau à deux dimensions n’est rien d’autre qu’une liste de listes qui sont toutes de la même longueur. Attention : pour faire une liste de n listes vides, il ne faut pas écrire tab = n*[[]], car on aurait n copies de la même liste : >>> tab = 4 * [ [ ] ] >>> tab [ 1 ] . append ( 42 ) >>> print tab [ [ 42 ] , [ 42 ] , [ 42 ] , [ 42 ] ] mais : >>> tab = [ ] >>> for i in range ( 4 ) : ... tab . append ( [ ] ) ... >>> tab [ 1 ] . append ( 42 ) >>> print tab [ [ ] , [ 42 ] , [ ] , [ ] ] Une variable de type list référence une zone mémoire. Si deux variables référencent une même zone mémoire, modifier l’une impacte l’autre : >>> grg1 = [ " Earth " , " : " , " harmless " ] >>> grg2 = grg1 >>> grg2 . insert (2 , " mostly " ) >>> print grg1 [ ’ Earth ’ , ’: ’ , ’ mostly ’ , ’ harmless ’] Extraire une tranche d’une liste réalise une copie d’une partie du contenu d’une liste (éventuellement la liste totale). Ce comportement est différent des tableaux de numpy (cf. section 2.4.1). >>> grg1 = [ " Earth " , " : " , " harmless " ] >>> grg2 = grg1 [ : ] >>> grg2 . insert (2 , " mostly " ) >>> print grg1 [ ’ Earth ’ , ’: ’ , ’ harmless ’] 3.1.2 Tuples Un tuple permet de stocker des successions de valeurs, et de les retrouver par leur index dans la structure, mais ne peut pas être modifié : on dit qu’il est non-mutable. Un tuple s’écrit entre parenthèses : tup = (6, 7). Il peut contenir tout type de données non-mutable, y compris d’autres tuples : tup = (6, 7.05, "Fish", (-5, "Dolphin")). On accède aux éléments d’un tuple par leurs index ou leurs tranches d’index exactement comme pour une liste. La fonction len() et l’opérateur in s’appliquent également. 3.1.3 Tables de hachage Une table de hachage (type dict) associe des clefs à des valeurs. Les clefs doivent produire un code de hachage qui permet de les retrouver dans la table, et donc ne peuvent pas être modifiées. Les types bool, int, float, str, tuple et frozenset conviennent, mais pas list, dict ou set. 52 Un type non-mutable peut disposer d’une fonction de hachage (son contenu ne variant pas, son code de hachage est une constante) alors qu’un type mutable ne le peut pas. Si tab est de type list, hash(tab) génère une erreur : TypeError: unhashable type: ’list’, alors que pour tup de type tuple, hash(tup) renvoie un entier sur 64 bits. Une table de hachage s’écrit entre accolades : thash = { key1 : val1 , key2 : val2 } L’ordre dans lequel les éléments sont ajoutés n’est pas préservé 1 . On peut récupérer la liste des clefs par thash.keys(), celle des valeurs par thash.values(), tester la présence d’une clef par : key in thash, supprimer une entrée par : del thash[key]. Si seules les clefs importent, et pas les valeurs, il peut être préférable d’utiliser les types set et frozenset. 3.2 3.2.1 Boucles et conditionnelles Conditionnelle Le branchement s’écrit : if cond: ... [elif cond: ... :]* [else: ... ]. 1 2 3 4 5 6 7 x = 42 if x != int(x): print "Non-integer" elif x<0: print "Negative integer" else: print "Positive integer" Entre if et les deux-points, il est possible de mettre toute expression booléenne, issue par exemple d’un opérateur de comparaison comme ==, <, >, <=, >= ou !=. Notez que l’opérateur de test d’égalité est == alors que l’opérateur d’affectation est =. Le test de non-égalité s’écrit!=. L’opérateur de test d’appartenance in construit également une expression booléenne. Il est possible de combiner des expressions booléennes par and, or et not. Il est possible d’inverser le comportement de in en le remplaçant par not in. L’instruction pass ne fait rien, et permet éventuellement de remplir une obligation syntaxique (Python n’accepte pas les blocs vides). if x < 0 or x > = 20 : pass else : print " % i has a value in [0 , 20 [ " % ( x ) Notez que prendre le complémentaire de l’expression booléenne fonctionne aussi, et est plus élégant : if not ( x < 0 or x > = 20 ) : print " % i has a value in [0 , 20 [ " % ( x ) 1. Python 3.3 devrait permettre un certain contrôle sur cet ordre d’insertion, mais ce n’est normalement pas le rôle d’une table de hachage. 53 ou encore : if x > = 0 and x < 20 : print " % i has a value in [0 , 20 [ " % ( x ) Il n’existe pas d’équivalent du switch ... case courant dans d’autres langages. Il convient d’utiliser une succession de elif pour choisir entre de multiples actions possibles en fonction de la valeur d’une variable. 3.2.2 Type booléen Il est également possible d’affecter la valeur d’une expression booléenne à une variable, lequel est donc de type bool, et peut prendre uniquement les deux valeurs True et False. >>> b = 8 > 3 and not 9 in (7 , 6 , " Fish " ) >>> print b True >>> print True or False True 3.2.3 Boucles La boucle for s’écrit : for var in tab : instructions Le corps de la boucle est exécuté une fois pour chaque élément de tab, lequel peut être tout objet contenant une succussion d’éléments. Il peut donc s’agir d’une liste, mais aussi d’une table de hachage, d’un tuple, d’un fichier, ou d’une fonction itératrice comme range(). La fonction range() permet (entre autres) de générer une succession d’indices pour for, mais aussi toute succession de nombres : range(start, end, step) correspond au vecteur Matlab/Octave start:step:end-step. La boucle while s’écrit : while cond : instructions Le corps de la boucle est exécuté chaque fois que la condition est vraie, et la boucle s’arrête dès que la condition est testée comme fausse. Il est donc possible que le corps de la boucle ne soit jamais exécuté. À l’intérieur d’une boucle while ou for, l’instruction continue permet de passer à l’itération suivante, et l’instruction break de quitter la boucle. L’exemple suivant comporte trois boucles. Dans la première, x prend successivement les valeurs contenues dans les cases de tab. La seconde et la troisième boucles ont exactement le même comportement : l’indice i prend successivement toutes les valeurs entières de 0 à len(tab)-1, c’est-à-dire les valeurs d’index valides pour tab. 1 2 3 tab = [6, 7, 2, 8, 42, 14] for x in tab: if 42%x!=0: 54 4 5 continue print x 6 7 8 for i in range(len(tab)): print i, tab[i] 9 10 11 12 13 14 15 3.2.4 i =0 while True: if i==len(tab): break print i, tab[i] i += 1 Boucle définissant une liste Il est immédiat de faire une boucle qui remplit une liste par des appels successifs à append() : y = [] for x in arange ( - 10 , 10 , 0 . 1 ) : y . append ( x ** 3 ) Toutefois ces appels à append() sont assez coûteux en temps de calcul, et la syntaxe est assez lourde. Python propose une alternative plaçant for à l’intérieur de la définition de la liste. y = [ x ** 3 for x in arange ( - 10 , 10 , 0 . 1 ) ] 3.3 Définition de fonction Il est bien sûr possible de créer ses propres fonctions, qui reçoivent éventuellement des paramètres à traiter, et renvoient un résultat via return. La fonction se déclare avec le mot-clé def. 1 2 3 # Computes the discriminant of ax^2+bx+c def discr(a, b, c): return b**2-4*a*c 4 5 6 7 8 9 10 11 3.3.1 # Computes the root(s) of ax^2+bx+c def roots(a, b, c): delta = discr(a, b, c) if delta >0: return (-b-delta**0.5)/(2*a), (-b+delta**0.5)/(2*a) elif delta ==0: return -b/(2.0*a) Variables locales et globales Si on affecte une valeur à une variable dans la fonction, on définit une variable locale. Cette variable n’existe qu’à l’intérieur de la fonction, et n’est plus accessible ensuite. Si une fonction f1 a une variable locale x et appelle une fonction f2, la variable x n’est pas disponible dans f2. Les paramètres de la fonction se comportent comme des variables locales à cette fonction. Si une variable existe déjà à l’extérieur de la méthode avec le même nom – c’est donc une variable globale – la variable locale masque la variable globale. 55 Les opérations réalisées à l’intérieur de la fonction portent uniquement sur la variable locale, et la variable globale de même nom n’est pas modifiée. Si on souhaite modifier une variable globale à l’intérieur d’une fonction, il convient de faire référence à cette variable globale par global foo. L’exécution du programme suivant affiche : 43 42 45 46 45 1 2 3 def f_loc(x): a =x print a, 4 5 6 7 8 def f_glob(x): global a a =x print a, 9 10 11 def g(x): print a, 12 13 14 15 16 17 18 3.3.2 a = 42 f_loc(43) g(44) f_glob(45) f_loc(46) g(47) Valeur(s) de retour Si return est suivi par plusieurs paramètres, ceux-ci sont placés dans un tuple. S’il n’y a pas de return (cas où δ < 0 dans roots), la valeur renvoyée est la valeur spéciale 2 None de type NoneType. Si on souhaite garantir que roots renvoie toujours un tuple, même vide, il faut traiter le cas δ < 0, et forcer la création d’un tuple quand δ = 0, par l’ajout d’une virgule ou la création explicite d’un tuple avec return tuple([-b/2/a]). 1 2 3 4 5 6 7 8 9 10 """ Computes the root(s) of ax^2+bx+c """ """ Returns a tuple """ def roots(a, b, c): delta = discr(a, b, c) if delta >0: return (-b-delta**0.5)/2/a, (-b+delta**0.5)/2/a elif delta ==0: return -b/2/a, else: return () Si la fonction renvoie un tuple de valeurs, il est possible d’affecter directement ces valeurs à pusieurs variables : 1 2 3 4 5 6 7 # Swaps x and y if x>y def order2(x, y): if x<y: return x,y else: return y,x print order2(7,6) Un return met fin à l’appel de la fonction. 2. Cette valeur spéciale est appelée null dans de nombreux autres langages. 56 3.3.3 Valeur par défaut des paramètres Il est possible de donner des valeurs par défaut aux arguments d’une fonction. 1 2 def norme(x, y=0, z=0): return (x**2+y**2+z**2)**0.5 Ainsi, des appels à norme(0,4,3), à norme(4,3), à norme(-5), à norme(4,z=3), ou à norme(0,z=4,y=3) renvoient 5.0. Un appel à norme(y=4, z=3) provoque une erreur : x n’a pas de valeur. 3.3.4 Fonctions récursives Une fonction est dite récursive si elle est capable de s’appeler elle-même. En général, une telle fonction modifie les paramètres d’appel, et dispose d’une condition d’arrêt qui, lorsqu’elle est vérifiée, fait qu’il n’y a pas d’appel récursif. Il est souvent 3 possible d’écrire une fonction récursive sous la forme d’une simple boucle, en général plus rapide à l’exécution, et moins simple à écrire et à comprendre. Factorielle, récursive et non-récursive : 1 2 3 4 5 6 # -*- coding: utf-8 -*def factorielle(n): if n<2: # Condition d’arrêt return 1 else: # Appel récursif return factorielle(n-1)*n 7 8 9 10 11 12 def factorielle_boucle(n): res = 1 for k in range(2, n+1): res *= k return res Recherche d’un zéro d’une fonction par dichotomie : 1 2 3 # -*- coding: utf-8 -*def f0(x): return x**3 4 5 6 def f1(x): return x**3-1 7 8 9 def f2(x): return x**3-2 10 11 12 13 14 def dichotomie(f, a, b, eps=1e-6): if b-a<=eps: # Condition d’arrêt :précision atteinte return (a, b) if f(a)*f(b)>0: # Condition d’arrêt :pas de racine 3. La fonction d’Ackermann ne peut pas être écrite sous forme de récursion primitive, ni sous forme de boucle : A(m, n) = n + 1 A(m − 1, 1) A(m − 1, A(m, n − 1)) 57 if m = 0 if m > 0 and n = 0 if m > 0 and n > 0. return None z = f(0.5*(a+b)) if z==0: # Condition d’arrêt :racine trouvée return 0.5*(a+b) if z*f(a)<0: # Racine sur la gauche return dichotomie(f, a, 0.5*(a+b), eps) else: # Racine sur la droite return dichotomie(f, 0.5*(a+b), b, eps) 15 16 17 18 19 20 21 22 23 24 25 26 27 print print print print "Racine "Racine "Racine "Racine de de de de f0 f1 f2 f0 entre entre entre entre -4 et 4 :", dichotomie(f0, -4, 4, 1e-10) -4 et 4 :", dichotomie(f1, -4, 4) -4 et 4 :", dichotomie(f2, -4, 4) 1 et 4 :", dichotomie(f0, 1, 4) Le résultat de ce programme est : Racine Racine Racine Racine de de de de f0 f1 f2 f0 entre entre entre entre -4 et 4 : 0.0 -4 et 4 : 1.0 -4 et 4 : (1.2599201202392578, 1.2599210739135742) 1 et 4 : None 3.4 Lancement d’un programme Il est possible d’appeler l’interpréteur Python à partir d’un environnement de développement intégré. Sous spyder cela se fait par F5, sous Eclipse par F11 ou Ctrl-F11. Il est possible de préciser les options de ligne de commande. On peut préciser les paramètres à passer au script dans le menu d’exécution. 3.4.1 Ligne de commande Pour exécuter un script foo.py à partir d’une terminal on tape simplement : python foo.py Sur un système Unix/Linux, si la première ligne du script est de la forme #!/usr/bin/python et que l’utilisateur a des droits en exécution dessus (bit x fixé), il suffit de taper foo.py. 3.4.2 Récupérer les arguments de la ligne de commande Le module sys donne accès à l’environnement du script. Notamment : argv : éléments de la ligne de commande. argv[0] est le nom du script, et les éléments suivants contiennent ses options et paramètres. Chaque élément est une chaîne de caractère, de type str : si l’argument souhaité est numérique, il convient de le transtyper par k = int(argv[1]) ou x = float(argv[1]). stdin : entrée standard, c’est-à-dire le clavier par défaut. stdout : sortie standard, c’est-à-dire le terminal d’où est exécuté le script par défaut. stderr : sortie d’erreur. 1 import sys 2 3 4 s =0 for x in sys.argv[1:]: 58 5 6 s += float(x) print "Total :",s Le script précédent s’appelant sum.py, un appel à : python sum.py 31 -9.5 20.5 provoquera l’affichage de : Total : 42.0 3.4.3 Type string Le type chaîne de caractères est nommé str en Python. Une chaîne de caractère est délimitée par des apostrophes 4 ou des guillemets. Certains caractères spéciaux, comme les tabulations ou les retours à la ligne, sont codés par caractères échappés : ’\t’ pour une tabulation, ’\n’ pour un retour à la ligne. Il faut échapper l’anti-slash par ’\\’ pour l’inclure dans une chaîne de caractères. >>> table = " Nom\t| Age\nMar\\/in\t|60E9" >>> print table Nom | Age Mar\/in |60E9 On peut demander à Python de ne pas interpréter les échappements dans une chaîne en la précédant par r. >>> table = r" Nom\t| Age\nMar\\/in\t|60E9" >>> print table Nom\t| Age\nMar\\/in\t|60E9 Il est possible de référencer l’un des caractères en considérant la chaîne comme une liste (s[3] pour le 4e caractère), mais chaque caractère est luimême de type str. Une chaîne de caractère est non-mutable : on ne peut pas modifier l’un de ses éléments par s[3]=’z’ (comme pour les tuples). On peut découper une chaîne de caractères en mots par la méthode split(). >>> s = " The answer is 42 . " >>> s . split () [ ’ The ’ , ’ answer ’ , ’ is ’ , ’ 42 . ’] On peut spécifier un délimiteur à split : >>> s . split ( ’e ’) [ ’ Th ’ , ’ answ ’ , ’r is 42 . ’] Des outils plus évolués de manipulation de chaîne de caractères sont accessibles par le module re (section 6.1). 4. Il existe plusieurs codages des apostrophes : p est différent de ’ ou de ‘. Le premier est un délimiteur valide pour les chaînes de caractères, et s’obtient par la touche “4” du clavier. Le second, plus joli, est généré par LATEX et se trouve dans les codes sources de ce document ; il vous posera des problèmes en cas de copier-coller depuis le document pdf. Le troisième, appelé anti-quote, sert à réaliser des appels au système d’exploitation ; il s’obtient par la touche “7” du clavier et peut apparaître comme plus semblable à 0 . 59 3.4.4 Opérateur % L’opérateur % permet de placer une ou plusieurs valeurs dans une chaîne : " Sinus de % i degr é s ~ : % 1 . 4f " % (x , math . sin ( x * math . pi / 180 ) ) Il doit y avoir autant d’éléments dans le tuple de valeurs à insérer que de caractères % dans la chaîne de caractères. La lettre qui suit le caractère % dans la chaîne détermine le type d’information à écrire : d ou i pour un nombre entier décimal ; x pour un nombre entier hexadécimal ; f, e, g, E ou G pour un nombre à virgule flottante ; s pour une chaîne de caractères, . . . . Les nombres qui précèdent permettent de maîtriser le nombre de caractères à écrire, et notamment la précision d’une nombre à virgule flottante. 3.4.5 Affichage L’instruction print permet l’écriture sur la sortie standard. Lors d’un appel à print x, si x n’est pas une chaîne de caractères, x est converti en chaîne de caractères par str(x). Il est possible 5 de demander l’affichage de plusieurs objets en les séparant par des virgules : print 6 , " x " , 7 , " = " , 6 * 7 Si le dernier objet est suivi d’une virgule, il n’y a pas de retour à la ligne : print 6 , " x " , 7 , " = " , print 6 * 7 3.5 Algorithmes de tri Définition 3.1 (Problème de tri). Soit une suite de n nombres : a1 , a2 , ..., an . Trier cette suite de nombres revient à trouver une permutation σ1 , ..., σn de 1, ..., n telle que aσ1 6 aσ2 6 ... 6 aσn . Le tri est un algorithme de base nécessaire pour bien d’autres algorithmes. Il permet de plus d’introduire la plupart des notions algorithmiques. Il existe de nombreux algorithmes de tris, plus ou moins simples à appréhender et plus ou moins efficaces. La plupart des ouvrages d’algorithmique, comme par exemple [CLRS94] décrivent les algorithmes de tris les plus classiques. Pour une analyse relativement détaillée des méthodes de tris les plus classiques, consulter [Knu73]. Dans tous les exemples suivants, l’algorithme de tri sera appliqué à un tableau d’entiers, mais il est possible de trier tout ensemble de données muni d’une relation d’ordre total (il est par exemple possible de trier un ensemble de mots suivant l’ordre lexicographique). 3.5.1 Tri par sélection Le tri par sélection est un tri extrêmement intuitif mais particulièrement inefficace. L’idée de base de l’algorithme est la suivante : rechercher le plus petit élément du tableau et l’échanger avec l’élément en première position. 5. Un des changements provoqués par Python 3 concerne print : cette instruction se comportera comme une fonction et s’appellera par print(6, "x", 7, "=", 6*7). 60 Recommencer avec le deuxième plus petit élément, et continuer jusqu’au n−1e plus petit élément. Exemple : 7* 1 1 1 1 2 2** 2 2 2 1* 7 7* 4 4 8 8 8 8* 7 4 4 4* 7* 8 Les éléments à permuter sont marqués d’un « * ». Remarquer que lors de la seconde étape l’élément 2 est permuté avec lui-même. L’algorithme du tri par sélection est décrit par l’algorithme 1. Il se traduit en Python par la fonction trisel(). Algorithme 1: Algorithme du tri par sélection Entrées : tab : entier[] Données : indMin, i, j, n : entier n ← taille(tab) ; pour i ∈ [0, n − 2] faire indM in ← i ; pour j ∈ [i + 1, n − 1] faire si tab[j] < tab[indM in] alors indM in ← j ; fin fin inverser(tab[indM in], tab[i]); fin 1 2 3 4 5 6 7 8 def trisel(tab): n = len(tab) for i in range(n-1): indmin =i for j in range(i+1, n): if tab[j] <tab[indmin]: indmin =j tab[i], tab[indmin] =tab[indmin], tab[i] Étude du tri par sélection La notion de complexité permet d’évaluer l’efficacité des algorithmes. Elle permet de répondre à la question : entre différents algorithmes réalisant une même tâche, quel est le plus rapide et dans quelles conditions ? La réponse à cette question, lorsqu’on souhaite légitimement s’abstraire des facteurs matériels est de quantifier le nombre d’opérations élémentaires nécessaires pour résoudre le problème. Définition 3.2. La complexité en temps est le nombre d’opération élémentaires permettant de résoudre un problème de taille N . 61 Donald Knuth fut un des premiers à l’appliquer systématiquement dans [Knu68]. La complexité est évaluée en fonction de la taille N des données d’entrée, où la taille est le nombre d’octets nécessaires pour stocker l’information des données d’entrée de l’algorithme. Les opérations élémentaires sont le plus souvent les opérations portant directement sur les données, comparaisons, affectation, modifications ou opérateurs arithmétiques. Il est courant d’omettre les opérations sur les indices lorsque leur nombre n’est pas d’un ordre de grandeur supérieur aux opérations sur les données : ces opérations sur les indices sont en général individuellement plus rapides, et beaucoup plus sensibles aux optimisations d’un compilateur. La complexité peut être évaluée dans le pire des cas, c’est-à-dire pour la répartition des données provoquant le plus d’opérations mais il est également possible de calculer la complexité moyenne, c’est-à-dire la moyenne des complexités pour toutes les données d’entrée possibles. Remarque. La complexité en moyenne est généralement beaucoup plus difficile à calculer que dans le pire des cas, mais elle donne souvent des informations plus pertinentes. Elle s’appuie toutefois sur une loi de probabilité sur les données qui peut être difficile à obtenir. Dans le cas du tri par sélection, remarquons que tous les cas sont identiques : la complexité dans le pire des cas est la complexité de tous les cas. Rechercher le plus petit élément force à parcourir le tableau et coûte donc n − 1 comparaisons. La recherche du deuxième plus petit élément coûte n − 2 Pn−1 i = n(n−1) comparaisons. Le coût total du tri par sélection est donc de i=1 . 2 Il est d’usage de ne garder que le terme prépondérant de la complexité et de ne pas prendre en compte les constantes multiplicatives. On dit alors que la complexité en moyenne et dans le pire des cas du tri par sélection est de Θ(n2 ). Remarque. La complexité de la plupart des algorithmes de tris intuitifs est de Θ(n2 ). La meilleure complexité possible pour un tri est de Θ(n log n) (voir la section 3.5.7 pour une démonstration de cette proposition). 3.5.2 Tri bulles Le tri bulles est un tri très populaire, très facile à mettre en œuvre. Son fonctionnement s’appuie sur des permutations répétées d’éléments contigus qui ne sont pas dans le bon ordre. Algorithme 2: Algorithme du tri bulles Entrées : tab : entier[] Données : i, j, n : entier n ← taille(tab); pour i ∈ [n − 1, 1] faire pour j ∈ [0, i − 1] faire si tab[j] > tab[j + 1] alors inverser(tab[j], tab[j + 1]); fin fin fin 62 L’idée de l’algorithme est de faire remonter le plus grand élément comme une bulle, puis de faire remonter l’élément suivant et ainsi de suite. Ainsi, lors de la première itération, chaque élément du tableau est comparé à son successeur, et permuté si nécessaire. À la fin de cette itération, le plus grand élément a été placé en fin de tableau. On recommence alors l’itération sur les n − 1 premières cases, puis sur les n − 2 premières, etc. Cet algorithme est décrit figure 2. 1 2 3 4 5 6 def tribulles(tab): n = len(tab) for i in range(n-1,0,-1): for j in range(0, i): if tab[j] >tab[j+1]: tab[j], tab[j+1] =tab[j+1], tab[j] Étude du tri bulles L’étude de la complexité du tri bulles est très simple, et similaire à celle du tri par sélection. Encore une fois, tous les cas sont équivalents, et la complexité en moyenne et dans le pire des cas valent Θ(n2 ). 3.5.3 Tri par insertion Le tri par insertion est une méthode de tri efficace dans le cas où le nombre d’éléments à trier est relativement faible ou dans le cas où le tableau est pratiquement ordonné. Le principe de l’algorithme (décrit dans la figure 3) est le suivant : le tableau à trier est découpé en deux parties. La partie de gauche est triée, et la partie de droite non triée. On insère alors successivement les éléments de la partie non triée à leur place dans la partie triée. Initialement, le premier élément est seul dans la partie triée. L’élément à insérer dans la partie triée est nommé clé. Afin d’insérer la clé à sa place dans la partie triée, il faut décaler tous les éléments qui lui sont supérieurs d’un cran vers la droite. Remarque. bien noter qu’il s’agit ici d’un décalage d’éléments et pas d’une permutation. Algorithme 3: Algorithme du tri par insertion Entrées : tab : entier[] Données : i, j, n, cle : entier n ← taille(tab) ; pour i ∈ [1, n − 1] faire cle ← tab[i] j ← i; tant que j > 0 et tab[j − 1] > cle faire tab[j] ← tab[j − 1] ; décrémenter j ; fin tab[j] ← cle fin Exemple d’insertion de clé : 63 1 3 4 2 Dans cet exemple, la clé vaut 2. Lors de son insertion, on passe par les étapes suivantes : 1 1 1 1 3 3 3 2 4 4 3 3 2 4 4 4 L’élément 4 est supérieur à la clé, et il a donc été copié d’un cran à droite (à ce moment, il apparaît donc deux fois). Ensuite, l’élément 3 a été copié un cran à droite. Enfin, la clé a été insérée à sa place. En utilisant la méthode d’insertion vu précédemment, voici un exemple d’étapes du tri par insertion. Considérons le tableau ci-dessous : 5 2 6 3 1 Dans ce tableau, 5 fait partie de la partie triée. La première étape consiste à insérer la clé 2 dans la partie triée. On obtient alors le tableau suivant, où les deux premières valeurs sont triées : 2 5 6 3 1 On poursuit alors les itérations avec comme clés successives 6, 3 et 1 : 2 2 1 1 2 3 4 5 6 7 8 9 5 3 2 6 5 3 3 6 5 1 1 6 def triins(tab): n = len(tab) for i in range(1,n): cle = tab[i] j =i while j>0 and tab[j-1]>cle: tab[j] =tab[j-1] j -= 1 tab[j] =cle Étude du tri par insertion Le tri par insertion est plus rapide que les tris précédents, bien que sa complexité moyenne soit du même ordre. Cependant, cette fois, tous les cas ne sont pas équivalents. Le pire des cas correspond à un tableau trié en ordre inverse. Dans ce cas, « insérer la clé à sa place » coûte Θ(i) avec i la taille du tableau trié à explorer. La complexité Pn−1 de l’algorithme est alors i=1 i = Θ(n2 ). Le meilleur des cas est un tableau trié. Dans ce cas, l’insertion de la clé coûte 1, et la complexité de l’algorithme devient Θ(n). 2 Nous admettrons que sa complexité moyenne est de Θ(n2 ) ( n4 comparaisons). 64 3.5.4 Tri shell Ce tri, proposé en 1959 par Donald L. Shell, constitue une variante optimisée du tri par insertion. Remarquons que le principal inconvénient du tri par insertion est le temps nécessaire pour déplacer une clé d’un côté à l’autre du tableau : il faut n déplacements d’une case. Cette remarque est à l’origine de l’algorithme du tri shell. Avant de trier le tableau, le tri shell va réaliser un pré-tri, c’est-à-dire qu’il va placer approximativement les éléments à leur place. Ce pré-tri va être réalisé sous la forme de tris par insertion avec différents pas h c’est-à-dire que chaque élément est décalé de h cases vers la droite (remarquons que pour h = 1, il s’agit d’un vrai tri par insertion). Un premier pré-tri avec un pas h grand permet de placer grossièrement les éléments. Ensuite, le pré-tri est affiné en diminuant la valeur de h. Finalement, un tri avec h = 1 (donc un tri par insertion) est effectué. Remarquons toutefois que le tri par insertion est effectué sur un tableau pratiquement trié et qu’il sera donc très efficace. Le choix de la suite de h est important et conditionne la rapidité du tri. Exemple de choix : Considérons la suite u0 = 1; un+1 = 3un + 1. Soit N la taille du tableau à trier. La suite de pas sera définie par : – h0 = le plus grand un tel que un < N hn − 1 – hn+1 = 3 Algorithme 4: Algorithme du tri Shell Entrées : tab : entier[] Données : i, j, n, cle,h : entier n ← taille(tab) ; h←1; tant que 3h + 1 < n # calcul de h0 ; faire h ← 3h + 1 ; fin tant que h > 0 faire pour i ∈ [h, n − 1] faire cle ← tab[i] # valeur à insérer dans la partie triée ; j ← i; tant que j > h et tab[j − h] > cle faire tab[j] ← tab[j −h] # décaler l’élément j−h de h crans à droite ; j ←j−h ; fin tab[j] ← cle # insérer cle à sa place ; fin h ← h/3 # équivalent à (h-1)/3 fin 65 Étude du tri shell Le tri shell est plus complexe à étudier que les tris vus jusqu’ici. Aussi, nous nous contenterons de donner les complexités sans les calculer. Pour une étude complète du tri shell, consulter [Knu73]. La complexité dans le pire des cas du tri shell dépend de la suite hi choisie. La meilleure suite est inconnue, mais il existe des suites 6 pour lesquelles la 4 3 complexité est de Θ(n 2 ) et d’autres 7 pour lesquelles elle est de Θ(n 3 ). La complexité en moyenne pour la meilleure suite de h est elle aussi inconnue (car la meilleure suite de h est inconnue). Pour hi = 2i+1 − 1, elle est de 3 O(n 2 ). Pour hi = 2p 3q , elle est de O(n log2 n). 3.5.5 Tri par segmentation, ou Quicksort Le tri par segmentation est un des tris les plus performants connus pour les tableaux de grande dimension. Couplé au tri par insertion, il devient le quicksort qui, comme son nom l’indique, est un tri particulièrement rapide et qui possède l’avantage d’avoir une complexité en espace de Θ(1) (i.e. indépendant de la taille des données). L’idée du tri par segmentation est la suivante : étant donné un tableau, on choisit un élément pivot, puis on place tous les éléments inférieurs au pivot à gauche et tous les éléments supérieurs à droite. On recommence ensuite l’opération sur les deux sous-tableaux délimités par l’élément pivot. 1 2 3 4 5 6 7 8 def triseg(tab): """Tri par segmentation avec une fonction de partitionnement""" if tab ==[]: return [] else: pivot = tab[0] lesser, equal, greater =partition(tab[1:], [], [pivot], []) return triseg(lesser) +equal +triseg(greater) 9 10 11 12 13 14 15 16 17 18 19 def partition(tab, lesser, equal, greater): while tab !=[]: head = tab.pop(0) if head <equal[0]: lesser.append(head) elif head >equal[0]: greater.append(head) else: equal.append(head) return (lesser, equal, greater) Le quicksort utilise de plus la contrainte suivante : si la taille du sous tableau est inférieure à un seuil n, on le trie en utilisant un tri par insertion. Les implantations les plus simples de cet algorithme utilisent la notion de récursivité vue au paragraphe 3.3.4. Dans le pire des cas, le quicksort a une complexité de Θ(n2 ). Par contre, sa complexité moyenne est de Θ(n log n) qui est la meilleure complexité atteignable par un algorithme de tri. De plus, les constantes multiplicatives (qui 6. hi = 2i+1 − 1 i 9.2i − 9.2 2 + 1 si i est pair 7. hi = i+1 8.2i − 6.2 2 + 1 si i est impair 66 n’apparaissent pas dans l’expression de la complexité) sont très faibles et font de ce tri l’un des plus performants connus. 3.5.6 Méthode sort des listes de Python Depuis 2002, Python utilise Timsort, une méthode hybride de tri issue du tri par fusion et du tri par insertion, développée par Tim Peters [Pet02]. Elle a un meilleur comportement dans le pire des cas que Quicksort. Contrairement aux tris des versions précédente, elle garantit un tri stable, c’est-à-dire qu’elle conserve l’ordre partiel des éléments considérés comme de même rang par l’opérateur de comparaison. L’implémentation de Timsort est faite en C, et est compilée dans la machine virtuelle python. Il s’agit alors d’un code adapté à l’architecture de la machine hôte. 3.5.7 Complexité des algorithmes de tris Lors de l’étude des différents algorithmes de tris, une question vient naturellement à l’esprit : quel est le meilleur tri existant ? Ou encore : existe-t-il une complexité minimale pour les algorithmes de tri ? Par meilleur algorithme de tri, on entend ici algorithme de tri qui minimise le nombre de comparaisons. Pour simplifier le problème, nous supposerons que toutes les valeurs à trier sont distinctes. Si l’ensemble contient n éléments, il existe n! permutations possibles. Comparer les éléments deux à deux revient à les placer dans un arbre binaire (voir figure 3.1 : les comparaisons effectuées par l’algorithme sont les nœuds de l’arbre (les cercles), les ordres finaux possibles sont ses feuilles (les rectangles). Trier un ensemble revient à parcourir l’arbre binaire jusqu’à une feuille. Proposition 3.1. La complexité dans le pire des cas d’un algorithme de tri basé sur des comparaisons ne peut pas être meilleure que Θ(n log n). 1<2 2<3 2<3 1<3 123 132 1<3 312 213 321 231 Figure 3.1 – Comparaisons permettant de trier 3 éléments Remarquons que cet arbre possède f = n! feuilles (toutes les permutations possibles). De plus, si l’arbre est de hauteur h, alors f 6 2h 67 (3.1) Dans le pire des cas, l’algorithme ne sera capable de conclure qu’après un nombre de tests égal à la hauteur de l’arbre. Soit c(n) le nombre de comparaisons nécessaires. Étant donné que ce nombre est un entier, on peut réécrire l’équation 3.1 en : c(n) > h = dlog2 (n!)e En utilisant la formule de Stirling, on obtient : dlog2 (n!)e = n log2 (n) − n 1 + log2 (n) + O(1) ln(2) 2 C’est-à-dire : c(n) = Θ(n log n) 3.6 Modules Des nombreuses extensions du langage sont disponibles dans des modules Python. Pour y accéder : import module Pour utiliser une fonction foo définie dans module : module . foo () On peut renommer le module alors qu’on l’importe : import module as m m . foo () Il est possible d’importer ce qui s’y trouve, et de rendre son usage transparent : from module import foo foo () On peut aussi tout importer par from module import * . . . mais cela peut entraîner des conflits entre modules. Pour utiliser la fonction, il suffit alors de faire foo(). 1 2 import sys, re from sys import stdin 3 4 5 6 7 8 9 test42 =re.compile(".*42.*") while True: s = stdin.readline() if test42.match(s): break print "No 42 in your line" Un module peut comporter des sous-modules. Ceux-ci sont organisés comme des sous-répertoires du répertoire courant ou d’un répertoire identifié comme contenant des modules Python pour la machine virtuelle 8 . 8. Sur un linux, ce répertoire est /usr/lib/python2.7 68 from matplotlib . backends . backend_pdf import PdfPages # Add some lines here to create a figure # Save it : pp = PdfPages ( " .. / img / mpl_basic10 . pdf " ) pp . savefig () pp . close () 3.7 Simulations numériques Le chapitre 2 a montré comment utiliser Python comme une calculatrice de haut niveau. Pour de nombreuses simulations numériques, boucles, tests et définitions de fonction peuvent être nécessaires. 3.7.1 Tirages aléatoires et statistiques Le module random permet des tirages aléatoires selon une grande variété de lois. Il est possible de faire coexister plusieurs générateurs pseudo-aléatoires dans un même programme. On crée un générateur par : rng = random.Random() On peut passer un paramètre à Random pour spécifier la graine (seed) initiale. Par défaut, la graine est initialisée avec l’horloge du système. random() : tirage aléatoire selon une loi uniforme sur [0, 1]. gauss(m, s) : tirage aléatoire selon une loi normale d’espérance m et de variance s2 . expovariate(a) : tirage aléatoire selon une loi exponentielle d’espérance 1/a. randint(a,b) : tirage d’un entier aléatoire entre a et b. Pour la fonction randint du module random, random.randint(a,b), les bornes sont inclues ; pour numpy.random.randint(a,b) seule la borne a est inclue, b est exclue. shuffle(x) : mélange la liste fournie en paramètre. seed(s) : utilise s comme nouvelle graine. Cela permet de jouer plusieurs fois la même séquence peudo-aléatoire. choice(x) : sélectionne un élément de x au hasard (loi équiprobable) sample(x, k) : sélectionne k éléments de x. 1 2 # -*- coding: utf-8 -*import random 3 4 rng = random.Random() 5 6 7 # Un dé a vingt faces d20 = rng.randint(1, 20) 8 9 10 11 12 # Somme de trois dés a six faces dsum3 = 0 for i in range(3): dsum3 +=rng.randint(1, 6) 13 14 # Somme des trois plus grands dés parmi 4 69 15 16 17 18 19 20 21 dices = [ ] for i in range(4): dices.append(rng.randint(1,6)) dices.sort() dsum34 =0 for d in dices[1:]: dsum34 +=d 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 # Constitution d’un jeu de 52 cartes values =range(2,11) values.extend(["V", "D", "R", "A"]) colors =["Ca", "Co", "Pi", "Tr"] deck = [ ] for v in values: for c in colors: deck.append(str(v)+"_"+c) # Copier cinq cartes au hasard dans le paquet # (elles y sont encore pour un autre tirage) hand1 = rng.sample(deck, 5) # Melanger le paquet rng.shuffle(deck) # Piocher les cinq dernières cartes (elles quittent le paquet) hand2=[ ] for i in range(5): hand2.append(deck.pop()) 40 41 42 43 44 45 46 47 48 49 3.7.2 # Faire 1000 tirages selon N(0,1) et compter les valeurs hors de # l’intervalle de confiance a 95% (on doit avoir n proche de 50) tab = [ ] for i in range(1000): tab.append(rng.gauss(0,1)) n =0 for x in tab: if abs(x)>1.96: n+=1 Résolution d’équation(s) Le module optimize de scipy permet notamment la résolution approchée d’équations ou de systèmes d’équation. Le programme suivant permet de résoudre l’équation x sin x = (3.2) 2 par une méthode de Newton avec un point de départ x0 = 2. 1 2 3 import numpy import scipy import scipy.optimize 4 5 6 def f(x): return numpy.sin(x)-x/2 7 8 9 10 start = 2 xsol = scipy.optimize.newton_krylov(f, start, f_tol=1e-14) print xsol Le programme suivant cherche un des huit points d’intersections entre les deux coniques : ( 2 x2 − y 2 − 1 = 0 (3.3) 2 2 2 x − 2 − 2y = 0 70 1 2 3 import numpy import scipy import scipy.optimize 4 5 6 7 8 9 10 # f prend en entree une liste de deux elements def f(xy): fval = [0, 0] fval[0] =xy[0]**2 -(xy[1]**2-1)**2 fval[1] =(xy[0]-2)**2 -2*xy[1] return fval 11 12 13 14 3.7.3 start = [1.8, 2] xysol = scipy.optimize.newton_krylov(f, start, x_tol=1e-7) print xysol, " :", f(xysol) Quadrature numérique Le module scipy.integrate fournit des fonctions pour la quadrature numérique ainsi que pour l’intégration numérique des équations différentielles. La fonction quad(f, a, b) est à pas adaptatif, et fournit un résultat sous Rb forme de tuple, dont le premier élément est une approximation de a f (x)dx où a ou b peuvent être infinis. Le second élément est une estimation de l’erreur absolue commise. 1 2 3 4 import import import import numpy as np scipy as sp scipy.integrate as integ math as m 5 6 7 def fN(x): return 1/(m.sqrt(2*m.pi))*m.exp(-0.5*x**2) 8 9 10 11 print "De -100 a 1.96: ", integ.quad(fN,-100,1.96) y, err =integ.quad(fN,-np.inf,1.96) print "De -oo a 1.96: %.12f plus ou moins %.2e"%(y, err) 12 13 14 15 16 17 18 tabx = np.arange(-100,1.96001,0.01) taby = np.zeros(len(tabx)) for i in range(len(tabx)): taby[i] = fN(tabx[i]) print "De -100 a 1.96 par les trapezes avec un pas de 1/100: ", print integ.trapz(taby, tabx) Résultat : De -100 a 1.96: (0.9750021048517795, 4.2103849945256025e-11) De -oo a 1.96: 0.975002104852 plus ou moins 3.62e-09 De -100 a 1.96 par les trapezes avec un pas de 1/100: 0.975001150321 Rb La fonction trapz(y,x) calcule une approximation de a f (x)dx par la méthode des trapèzes en se basant sur les vecteurs x et y. Elle sera à pas constant si les points de x suivent une telle répartition, par exemple générée par numpy.arange(). 3.7.4 Intégration numérique des ODE La fonction odeint permet l’intégration numérique d’une équation différentielle ou d’un système d’équations différentielles du premier ordre. Elle 71 utilise la bibliothèque odepack, écrite en Fortran, qui implémente une méthode adaptative : les points effectivement calculés par la méthode ne sont pas seulement les points demandés pour la sortie. L’équation du pendule est : ∂2θ g = − sin θ 2 ∂t ` (3.4) On la passe au premier ordre en introduisant une variable u(t) = ! ∂ θ ∂t u = u g − ` sin θ ∂θ ∂t : ! (3.5) On obtient ainsi la fonction de R2 dans R2 qui est la dynamique (ou équation d’état) du système. 1 2 3 import numpy as np from scipy.integrate import odeint from pylab import * 4 5 6 7 def dyn(x, t, ell, g): (th, u) =x return (u, -g/ell*np.sin(th)) 8 9 10 x0=(3*np.pi/4, 0) t = np.arange(0, 10, 0.01) 11 12 x = odeint(dyn, x0, t, args=(1, 9.81)) 13 14 plot(t, x[:,0], color="blue", linewidth=2.5, linestyle="-") 15 16 17 18 19 20 21 22 ax = gca() ax.spines[’right’].set_color(’none’) ax.spines[’top’].set_color(’none’) ax.xaxis.set_ticks_position(’bottom’) ax.spines[’bottom’].set_position((’data’,0)) ax.yaxis.set_ticks_position(’left’) ax.spines[’left’].set_position((’data’,0)) 23 24 25 xlim(0, 10.3) xticks(range(0, 11)) 26 27 28 29 ylim(x[:,0].min()*1.1, x[:,0].max()*1.1) yticks(arange(-np.pi, np.pi+1e-12, np.pi/4), [r’$\frac{%i}{4}\pi$’%(i) for i in range(-4,5)]) 30 31 32 #savefig("odeint.png",dpi=200) show() La fonction odeint() demande au moins trois paramètres : la dynamique de l’équation différentielle, qui doit être une fonction (dyn dans l’exemple précédent), le vecteur d’état à l’instant initial (x0 dans l’exemple précédent), et les instants pour lesquels on souhaite calculer le vecteur d’état (t dans l’exemple précédent). Ici, la dynamique demande deux paramètres supplémentaires, que l’on passe via le paramètre args de odeint. Si l’on dispose du jacobien de la dynamique sous forme de fonction, on peut le fournir à odeint par le paramètre Dfun, ce qui peut améliorer et/ou accélérer le schéma numérique. 72 Figure 3.2 – Tracé de θ en fonction du temps 73 4 Bibliothèques de haut niveau Sommaire 4.1 4.2 4.3 Gestion du temps par time Utilisation des fichiers en mode texte PIL : traitement d’image 4.3.1 Fichier image 4.3.2 Transformations 4.3.3 Exemple de manipulations 4.3.4 Lien avec d’autres objets Python 4.4 Sympy 4.4.1 Notion de symbole 4.4.2 Fractions 4.4.3 Limites 4.4.4 Dérivation 4.4.5 Développements limités 4.4.6 Séries 4.4.7 Intégration 4.4.8 Équations différentielles 4.4.9 Équations algébriques 4.4.10 Algèbre matricielle 74 74 75 75 76 79 81 82 82 83 84 84 85 85 85 86 86 86 D e nombreuses fonctionnalités de haut niveau ont été développées en Python, ce qui permet d’obtenir des programmes très puissants en peu de lignes. 4.1 Gestion du temps par time Le package time permet de gérer le temps. time() : renvoie le nombre de secondes depuis le 1er janvier 1970, sous forme d’un float. sleep(sec) : met le processus en pause. Le paramètre sec est le temps de pause exprimé en secondes ; ce peut être un nombre à virgule flottante. 4.2 Utilisation des fichiers en mode texte On crée un objet fichier Python avec la fonction open() : >>> MonFichier = open ( " NomDuFichier " , ’r ’) 74 Le mode peut être ’r’ (read, en lecture seule), ’w’ (en écriture seule), ’a’ pour rajouter à la fin du fichier, . . . . D’autres modes (mixte lexture/écriture, etc...) existent. La lecture des fichiers texte (i.e. non binaires) se fait avec les fonctions read(), readline() et readlines() : >>> MonFichier . read () # lit la totalit é du fichier sous forme d ’ une cha î ne de caract è res >>> MonFichier . readline () # lit la ligne suivante du fichier , sous forme d ’ une cha î ne de caract è res >>> MonFichier . readlines () # lit toutes les lignes du fichier sous forme d ’ une liste de cha î nes de caract è res >>> Monfichier . write ( ’ toto ’) # é crit la cha î ne de caract è re ’ toto ’ dans le fichier Pour lire une liste de nombres contenu dans une ligne, il suffit de lire cette ligne avec la fonction readline(), puis de séparer les nombres avec la méthode split() de str. Par exemple : >>> f = open ( " tmp0 " , ’r ’) >>> ligne = f . readline () # lecture d ’ une ligne >>> print ligne # É criture de la ligne sous forme d ’ une cha î ne de caract è res Toto 1 . 8 9 . 2 >>> print ligne . split () # On s é pare les é l é ments - > liste [ ’ Toto ’ , ’0 . 2 ’ , ’9 . 2 ’] >>> print float ( ligne . split () [ 1 ] ) # Conversion en nombre r é el de ’0 . 2 ’ >>> f . close () # referme le fichier La commande help(file) permet de lister l’aide sur l’utilisation des fichiers. 4.3 PIL : traitement d’image Il existe beaucoup de possibilités pour réaliser du traitement d’image sous Python. Par exemple, sous MatPlotLib certaines fonctionnalités 1 permettent de traiter les images. Il existe aussi des bibliothèques dédiées au traitement d’image de niveau professionnel avec lesquelles on peut travailler sous Python (par exemple OpenCV 2 . L’avantage principal de la bibliothèque PIL est la simplicité de mise œuvre. Cette section est fortement inspirée d’un tutoriel réalisé par Jean-Louis Bicquelet-Salaün [BS09]. 4.3.1 Fichier image Pour accéder au fichier image et pour connaître certaines informations sur l’image : 1 # -*- coding: utf-8 -*- 2 3 from PIL import Image 4 5 filename =’../img/pil_colibri.jpeg’ 6 1. matplotlib.org/users/image_tutorial.html 2. www.neuroforge.co.uk/index.php/getting-started-with-python-a-opencvl 75 7 8 im = Image.open(filename) print file, im.format, "%dx%d" % (im.size), (im.mode) On peut signaler que la classe image de la bibliothèque PIL est un descendant de la classe file. À l’exécution, nous obtenons : .. / img / pil_colibri . jpeg JPEG 400x398 RGB Pour visualiser l’image : 1 2 3 # -*- coding: utf-8 -*from PIL import Image import ImageFilter 4 5 6 filename =’../img/pil_colibri.jpeg’ im = Image.open(filename) 7 8 im.show() Cette image fait partie de l’exposition IMAGINARY organisée par l’institut Mathematisches Forschungsinstitut Oberwolfach. 4.3.2 Transformations Décomposition en 3 composantes On décompose l’image en trois images différentes correspondant aux trois composantes (rouge, vert, bleu) de l’image originale : 1 # -*- coding: utf-8 -*- 2 3 4 5 6 7 8 9 import Image img = Image.open("../img/pil_colibri.jpeg") # Ouverture de l’image r,g,b = img.split() # Récupération des différentes composantes de l’image # Sauvegarde des images des différentes composantes r.save(’../img/pil_colibri_r.jpg’) g.save(’../img/pil_colibri_v.jpg’) b.save(’../img/pil_colibri_b.jpg’) Convertion du format Avec PIL, il est possible de convertir une image sous les trois formats : PNG, JPEG, BMP. 1 2 3 # -*- coding: utf-8 -*from PIL import Image img = Image.open("../img/pil_colibri.jpeg") # Ouverture de l’image 76 Figure 4.1 – Niveaux de rouge, vert et bleu de l’image originale. 4 5 6 img.save("../img/pil_colibri.png", "PNG") img.save("../img/pil_colibri.bmp", "BMP") Image miniature Pour créer une miniature à partir de l’image originale : 1 2 # -*- coding: utf-8 -*from PIL import Image 3 4 5 6 7 size=128,128 im = Image.open(’../img/pil_colibri.jpeg’) im.thumbnail(size, Image.ANTIALIAS) im.save(’../img/pil_colibri_small.png’, "PNG") Pour réaliser cette opération sur tout un répertoire : 1 2 3 # -*- coding: utf-8 -*from PIL import Image import glob, os 4 5 size = 128, 128 6 7 8 9 10 11 for infile in glob.glob("*.png"): filename, ext =os.path.splitext(infile) im = Image.open(infile) im.thumbnail(size, Image.ANTIALIAS) im.save(filename +"_small.png", "PNG") Transformation isométrique On peut réaliser une rotation à l’aide de la fonction rotate() : 1 2 # -*- coding: utf-8 -*from PIL import Image 3 4 im = Image.open(’../img/pil_colibri.jpeg’) 77 5 6 out = im.rotate(45) out.save(’../img/pil_colibri_rot45.png’) Il est possible de réaliser d’autres transformations en utilisant transpose() et en utilisant des mots clé comme : FLIP_LEFT_RIGHT, FLIP_TOP_BOTTOM, ROTATE_90, ROTATE_180, ROTATE_270,... 1 2 # -*- coding: utf-8 -*from PIL import Image 3 4 5 6 im = Image.open(’../img/pil_colibri.jpeg’) out = im.transpose(Image.FLIP_LEFT_RIGHT) out.save(’../img/pil_colibri_flipLR.png’) Redimensionnement Pour redimensionner une image, on utilise resize() : 1 2 # -*- coding: utf-8 -*from PIL import Image 3 4 5 6 im = Image.open(’../img/pil_colibri.jpeg’) out = im.resize((100,200)) out.save(’../img/pil_colibri_resize.png’) Filtrage Le filtrage d’une image se réalise à l’aide de la fonction filter() et le module ImageFilter qui propose de nombreux filtres (filtres min, max, médian, flou, . . . 3 ). 3. http://www.pythonware.com/library/pil/handbook/imagefilter.htm 78 # -*- coding: utf-8 -*from PIL import Image import ImageFilter 1 2 3 4 im = Image.open(’../img/pil_colibri.jpeg’) out = im.filter(ImageFilter.BLUR) out.save(’../img/pil_colibri_blur.png’, "PNG") out2 = im.filter(ImageFilter.MinFilter) out2.save(’../img/pil_colibri_min.png’, "PNG") out3 = im.filter(ImageFilter.MedianFilter) out3.save(’../img/pil_colibri_median.png’, "PNG") 5 6 7 8 9 10 11 Figure 4.2 – Image originale (gauche) et filtrée par blur (droite). 4.3.3 Exemple de manipulations Concaténation d’images Pour concaténer une image avec son symétrique : 1 2 # -*- coding: utf-8 -*from PIL import Image 3 4 5 6 7 8 9 10 11 12 im = Image.open(’../img/pil_colibri.jpeg’) w,h=im.size box = (0, 0, w, h) src = im.crop(box) out = im.resize((w*2,h)) out.paste(src,(0,0,w,h)) src=im.transpose(Image.FLIP_LEFT_RIGHT) out.paste(src,(w,0,2*w,h)) out.save(’../img/pil_colibri_plussym.png’) 79 Dessiner sur une image 1 2 3 # -*- coding: utf-8 -*from PIL import Image import ImageDraw 4 5 6 7 8 9 10 11 12 13 im = Image.new("RGB", (400,200), "lightgrey") draw = ImageDraw.Draw(im) w,h=im.size draw.ellipse((w/4,h/4) +(3*w/4,3*h/4), fill="green") draw.line((0, 0) +im.size, width=10,fill="red") offsetx=100 draw.rectangle(((100+offsetx,100),(130+offsetx,200)),fill="gray") del draw im.save("../img/pil_dessin.png", "PNG") Image multiple 1 2 3 # -*- coding: utf-8 -*from PIL import Image import ImageDraw 4 5 6 im = Image.new("RGB", (500,400), "white") draw = ImageDraw.Draw(im) 80 7 8 9 10 11 12 13 4.3.4 1 2 3 4 into = Image.open("../img/pil_colibri_small.png") w,h=into.size im.paste(into, (0,0,w,h)) im.paste(into, (300,0,300+w,h)) im.paste(into, (200,200,200+w,200+h)) del draw im.save("../img/pil_multiple.png") Lien avec d’autres objets Python # -*- coding: utf-8 -*from PIL import Image import ImageFilter import numpy as np 5 6 7 filename =’../img/pil_colibri.jpeg’ im = Image.open(filename) 8 9 10 11 12 13 14 15 16 17 18 19 20 21 # ag recoit les pixels de l’image im en niveaux de gris ag = np.zeros((im.size[1], im.size[0])) i =0 j =0 for x in im.getdata(): ag[j, i] =0.21*x[0]+0.72*x[1]+0.07*x[2] i += 1 if i == im.size[0]: # Passage a la ligne suivante i =0 j += 1 ig = Image.fromarray(ag) # Conversion du tableau ag en Image ig.show() ig.convert(’RGB’).save(’../img/pil_colibri_gray.png’) 22 23 24 25 26 27 28 29 30 31 32 33 # On arrondit le niveau de gris a 16 pres ag = 16*np.round(ag/16) # On ajoute des lignes blanches verticales for i in range(0,im.size[0], 16): ag[:,i] =255 # On ajoute des lignes noires horizontales for j in range(0,im.size[1], 16): ag[j,:] =0 it = Image.fromarray(ag) it.show() it.convert(’RGB’).save(’../img/pil_colibri_gray_16.png’,’png’) Figure 4.3 – Deux images traitées via numpy 81 La méthode getdata() permet de récupérer le contenu de l’image, sous forme d’un itérateur sur les pixels de l’image. Elle permet de réécrire simplement les pixels dans une liste ou un tableau. La fonction fromarray permet de transformer un tableau, notamment un tableau numpy en Image. Un passage par la méthode convert peut être nécessaire pour garantir le format interne de l’image avant écriture. 4.4 Sympy La bibliothèque Sympy ouvre au langage Python la possibilité de réaliser du calcul mathématique symbolique. Il est important de souligner que cette bibliothèque fait actuellement l’objet d’une activité très importante de développement et qu’il est sans doute à attendre dans un avenir proche des améliorations significatives. D’ores et déjà, Sympy offre un nombre important de fonctionnalités qui le rapproche de logiciels classiques du calcul symbolique comme Mapple ou Mathematica. Cette section reprend des extraits du tutoriel de Sympy qui peut être lancé de façon interactive sur le site : docs.sympy.org/0.7.2/tutorial.html. Voici un premier exemple simple d’application au calcul fractionnel : >>> from sympy import Rational >>> a = Rational (1 , 2 ) >>> a 1/2 >>> a * 2 1 >>> Rational ( 2 ) ** 50 / Rational ( 10 ) ** 50 1/ 88817841970012523233890533447265625 On peut également écrire : >>> R = Rational >>> R (1 , 2 ) 1/2 >>> R ( 1 ) / 2 # R ( 1 ) est SymPy Integer et Integer / int donne un Rational 1/2 Sympy permet aussi l’utilisation d’éléments mathématiques comme π, e ou ∞: >>> from sympy import pi , E >>> pi ** 2 pi ** 2 >>> pi . evalf () 3 . 14159265358979 >>> ( pi + E ) . evalf () 5 . 85987448204884 >>> from sympy import oo >>> oo > 99999 True >>> oo + 1 oo 4.4.1 Notion de symbole Avec Sympy, si l’on souhaite travailler avec des variables symboliques comme x, y, z, r, θ, φ etc, il est nécessaire de les déclarer explicitement : 82 >>> from sympy import Symbol >>> x = Symbol ( ’x ’) >>> y = Symbol ( ’y ’) Certains symboles sont déjà prédéfinis dans la sous-bibliothèque abc : >>> from sympy . abc import x , theta >>> x x >>> theta theta Pour créer des symboles, on peut également utiliser la fonction var() : >>> from sympy import symbols , var >>> a , b , c = symbols ( ’a ,b , c ’) >>> d , e , f = symbols ( ’d : f ’) >>> var ( ’g : h ’) (g , h ) >>> var ( ’g : 2 ’) ( g0 , g1 ) Voici quelques opérations simples : >>> x + y + x - y 2*x >>> ( x + y ) ** 2 ( x + y ) ** 2 >>> (( x + y ) ** 2 ) . expand () x ** 2 + 2 * x * y + y ** 2 Lors d’opérations, il est possible de faire prendre à une variable une valeur numérique ou encore la remplacer par une autre expression en utilisant subs() : >>> (( x + y ) ** 2 ) . subs (x , 1 ) ( y + 1 ) ** 2 >>> (( x + y ) ** 2 ) . subs (x , y ) 4 * y ** 2 >>> (( x + y ) ** 2 ) . subs (x , 1 - y ) 1 Pour améliorer la présentation, on lancera : >>> from sympy import init_printing >>> init_printing ( use_unicode = True , wrap_line = False , no_global = True ) 4.4.2 Fractions Développement de fraction : >>> from sympy import apart >>> from sympy . abc import x , y , z >>> 1 / ( ( x + 2 ) * ( x + 1 ) ) 1 --------------(x + 1)*(x + 2) >>> apart ( 1 / ( ( x + 2 ) * ( x + 1 ) ) , x ) 1 1 - ----- + ----x + 2 x + 1 >>> ( x + 1 ) / ( x - 1 ) 83 x + 1 ----x - 1 >>> apart ( 1 / ( ( x + 2 ) * ( x + 1 ) ) , x ) 1 1 - ----- + ----x + 2 x + 1 Mise sous dénominateur commun : >>> from sympy import together >>> together ( 1 / x + 1 / y + 1 / z ) x*y + x*z + y*z --------------x*y*z >>> together ( apart (( x + 1 ) / ( x - 1 ) , x ) , x ) x + 1 ----x - 1 >>> together ( apart ( 1 / ( ( x + 2 ) * ( x + 1 ) ) , x ) , x ) 1 --------------(x + 1)*(x + 2) 4.4.3 Limites La fonction limit(function, variable, point) permet de calculer la limite d’une fonction définie par son expression symbolique : >>> >>> >>> 1 >>> oo >>> 0 >>> 1 from sympy import limit , Symbol , sin , oo x = Symbol ( " x " ) limit ( sin ( x ) /x , x , 0 ) limit (x , x , oo ) limit ( 1 /x , x , oo ) limit ( x ** x , x , 0 ) 4.4.4 Dérivation Le calcul des dérivées s’effectue à l’aide de la fonction diff(func, var) : >>> from sympy import diff , Symbol , sin , tan >>> x = Symbol ( ’x ’) >>> diff ( sin ( x ) , x ) cos ( x ) >>> diff ( sin ( 2 * x ) , x ) 2 * cos ( 2 * x ) >>> diff ( tan ( x ) , x ) 2 tan ( x ) + 1 Dérivation d’ordre supérieur : >>> diff ( sin ( 2 * x ) , x , 1 ) 2 * cos ( 2 * x ) >>> diff ( sin ( 2 * x ) , x , 2 ) - 4 * sin ( 2 * x ) >>> diff ( sin ( 2 * x ) , x , 3 ) 84 - 8 * cos ( 2 * x ) 4.4.5 Développements limités Voici un exemple : >>> from sympy import Symbol , cos >>> x = Symbol ( ’x ’) >>> cos ( x ) . series (x , 0 , 10 ) 2 4 6 8 x x x x 1 - - - + - - - - - - + - - - - - + O ( x ** 10 ) 2 24 720 40320 >>> ( 1 / cos ( x ) ) . series (x , 0 , 10 ) 2 4 6 8 x 5*x 61 * x 277 * x 1 + - - + - - - - + - - - - - + - - - - - - + O ( x ** 10 ) 2 24 720 8064 >>> y = Symbol ( " y " ) >>> e = 1 / ( x + y ) >>> s = e . series (x , 0 , 5 ) >>> print ( s ) 1 / y - x / y ** 2 + x ** 2 / y ** 3 - x ** 3 / y ** 4 + x ** 4 / y ** 5 + O ( x ** 5 ) 4.4.6 Séries >>> from sympy import summation , oo , symbols , log >>> i , n , m = symbols ( ’i n m ’ , integer = True ) >>> summation ( 2 * i - 1 , (i , 1 , n ) ) n ** 2 >>> summation ( 1 / 2 ** i , (i , 0 , oo ) ) 2 >>> summation ( 1 / log ( n ) ** n , (n , 2 , oo ) ) Sum ( log ( n ) ** ( - n ) , (n , 2 , oo ) ) >>> summation (i , (i , 0 , n ) , (n , 0 , m ) ) m ** 3 / 6 + m ** 2 / 2 + m / 3 >>> from sympy . abc import x >>> from sympy import factorial >>> summation ( x ** n / factorial ( n ) , (n , 0 , oo ) ) exp ( x ) 4.4.7 Intégration Pour le calcul de primitive : >>> from sympy import integrate , erf , exp , sin , log , oo , pi , sinh , symbols >>> x , y = symbols ( ’x , y ’) >>> integrate ( 6 * x ** 5 , x ) x ** 6 >>> integrate ( sin ( x ) , x ) - cos ( x ) >>> integrate ( exp ( - x ** 2 ) * erf ( x ) , x ) pi ** ( 1 / 2 ) * erf ( x ) ** 2 / 4 Pour le calcul d’intégrales définies ou impropres : 85 >>> 0 >>> 1 >>> 1 >>> -1 integrate ( x ** 3 , (x , -1 , 1 ) ) integrate ( sin ( x ) , (x , 0 , pi / 2 ) ) integrate ( exp ( - x ) , (x , 0 , oo ) ) integrate ( log ( x ) , (x , 0 , 1 ) ) 4.4.8 Équations différentielles >>> from sympy import Function , Symbol , dsolve >>> f = Function ( ’f ’) >>> x = Symbol ( ’x ’) >>> f ( x ) . diff (x , x ) + f ( x ) f ( x ) + Derivative ( f ( x ) , x , x ) >>> dsolve ( f ( x ) . diff (x , x ) + f ( x ) , f ( x ) ) f ( x ) = = C1 * cos ( x ) + C2 * sin ( x ) 4.4.9 >>> >>> >>> [1 , >>> {x: Équations algébriques from sympy import solve , symbols x , y = symbols ( ’x , y ’) solve ( x ** 4 - 1 , x ) -1 , -I , I ] solve ( [ x + 5 * y - 2 , - 3 * x + 6 * y - 15 ] , [x , y ] ) -3 , y : 1 } 4.4.10 Algèbre matricielle >>> from sympy import Matrix , Symbol >>> Matrix ( [ [1 , 0 ] , [0 , 1 ] ] ) [1 , 0 ] [0 , 1 ] >>> x = Symbol ( ’x ’) >>> y = Symbol ( ’y ’) >>> A = Matrix ( [ [1 , x ] , [y , 1 ] ] ) >>> A [1 , x ] [y , 1 ] >>> A ** 2 [x*y + 1, 2*x] [ 2 *y , x * y + 1 ] 86 5 Langage objet Sommaire 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 Variables d’instance Méthodes Constructeur 5.3.1 Transtypage Destructeur : ramasse-miette Surcharge d’opérateur Documention et interrogation 5.6.1 Conventions de nommage Héritage 5.7.1 Appartenance à une classe Exemple : géométrie par les objets 88 88 88 89 89 90 90 91 91 91 92 P ython est un langage objet, et tous les types vus précédemment sont des types objet. Il est possible d’en définir d’autres, que ce soit comme descendant de types existant ou de façon autonome, et de les utiliser aussi simplement. Une classe est un type objet. Un objet est une variable de ce type ; on dira aussi qu’un objet est une instance de la classe. Un objet regroupe des variables d’instance dont les valeurs définissent l’état de l’objet, et des méthodes permettant d’agir sur ces variables d’instance, aussi bien en lecture qu’en écriture. Une classe se définit par le mot-clef class. Le constructeur est une fonction particulère qui s’appelle toujours __init__(). L’héritage se fait par class MyClass(Class1, Class2, ...). 1 2 3 4 5 6 7 8 class Complex: def __init__(self, x_init, y_init): self.x =x_init self.y =y_init def module(self): return (self.x**2+self.y**2)**0.5 z = Complex(3,4) print z.module() 87 5.1 Variables d’instance Les variables d’instances sont attachées à chaque instance de la classe. Elles caractérisent l’objet, et portent toute son information. Dans l’exemple précédent, les deux variables d’instance d’un objet de type Complex sont x et y. Il n’est pas nécessaire d’en spécifier le type ; comme toute variable en Python, elles reçoivent ou modifient leur type lors des opérations d’affectation. On peut accéder aux variables d’instance x et y d’un objet z par z.x et z.y, en écriture comme en lecture. 5.2 Méthodes Une méthode est une fonction particulière, attachée à une classe. Le premier paramètre d’une méthode est toujours self. Si obj est un objet de la classe définissant la méthode foo, il est équivalent d’appeler la méthode foo(self, x) par obj.foo(x) ou foo(obj, x). La notation obj.foo(x) est celle de la programmation objet : on demande à l’objet obj d’utiliser son service foo. 5.3 Constructeur On crée un objet de classe MaClasse par : mon_objet = MaClasse ( p1 , p2 ) Les paramètres p1 et p2 sont passés au constructeur de MaClasse, et servent à initialiser les variables d’instance de mon_objet. Le constructeur est une méthode particulière, qui est appelée juste après la réservation de l’espace mémoire pour l’objet. Il s’appelle toujours __init__(). Son premier paramètre est l’objet en cours de création, et les paramètres suivants servent à l’initialiser. À l’issue de l’exécution du constructeur, toutes les variables d’instance doivent avoir reçu une valeur. Comme pour les fonctions (section 3.3), les paramètres du constructeur peuvent recevoir des valeurs par défaut. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Complex: def __init__(self, x_init, y_init=0): """ builder; give values to instance variables x et y. """ self.x =x_init self.y =y_init def module(self): """ returns the absolute value of the Complex. """ return (self.x**2+self.y**2)**0.5 def set_module(self, r): """ modifies the absolute value but keeps the argument. """ r_act = self.module() self.x *=r/r_act self.y *=r/r_act z1 = Complex(3, 4) z1.set_module(10) print "z1 :", z1.x, z1.y z2 = Complex(-3) z2.set_module(10) 88 19 5.3.1 print "z2 :", z2.x, z2.y Transtypage En section 3.4.2, nous avons vu un programme récupérant les arguments de la ligne de commande pour en faire la somme. 1 import sys 2 3 4 5 6 s =0 for x in sys.argv[1:]: s += float(x) print "Total :",s En ligne 5, on effectue un transtypage de x vers un type float. En pratique, on appelle le constructeur de float avec un paramètre de type str, et ce constructeur fournit une nouvelle instance de float dont la valeur numérique est construite en analysant le contenu de la chaîne de caractères x. Si la conversion n’est pas possible, c’est-à-dire dire si la valeur ou le type des paramètres fournis ne conviennent pas, une exception est levée : >>> float ( " quarante - deux " ) ValueError : could not convert string to float : quarante - deux >>> float ( [ 42 ] ) TypeError : float () argument must be a string or a number 5.4 Destructeur : ramasse-miette En Python, lorsqu’un objet n’est plus utile, il n’est pas nécessaire de le détruire explicitement 1 . Chaque objet dispose d’un compteur de référence, qui détermine le nombre de fois que la zone mémoire occupée par l’objet est référencée par le programme. Ce peut être directement par une variable du programme, par une variable locale d’une fonction en cours d’exécution, ou par une variable d’insance d’un autre objet. Si ce compteur de référence tombe à 0, la zone mémoire utilisée par l’objet ne pourra plus jamais être référencée. Un ramasse-miettes (un objet de type garbage collector attaché à chaque programme) est chargé de désallouer la mémoire des objets dont le compteur de références tombe à 0. Un cycle de référence survient si A a une variable d’instance dont la valeur est B, et B a une variable d’instance dont la valeur est A. Lorsque ni A ni B ne sont plus référencés par le programme, chaque objet a un compteur de références de 1, mais aucun n’est accessible, et leur espace mémoire doit être désalloué. Si un objet C se référence lui-même, l’effet est le même. Si le ramassemiette utilise uniquement le mécanisme de comptage de références, ces zones mémoires sont à la fois inutiles et allouées : c’est une fuite de mémoire, et donc une perte de mémoire disponible. Depuis la version 2.0 de Python, le ramasse-miettes est également capable de détecter les cycles de références. Cette fonctionnalité est cependant assez lourde – algorithmiquement – et n’est lancée que de temps en temps, ou lorsque certains seuils de quantité d’objets actifs dans le programme sont atteints. 1. En C++; il faut détruire explicitement un objet par un appel à son destructeur pour que la mémoire qu’il occupe soit à nouveau disponible. 89 5.5 Surcharge d’opérateur Les opérateurs sont des méthodes de leurs opérandes. Il est possible de les surcharger quand on définit un nouvel objet. • str(x) : x.__str__(), • repr(x) : x.__repr__() • a+b : a.__add__(b), b.__radd__(a) • a-b : a.__sub__(b), b.__rsub__(a) • * : a.__mul__(b), b.__rmul__(a) • size(x) : x.__sizeof__(), • <,>, <=,>=,==,!= : analyser le résultat de a.__cmp__(b) . . . et quantité d’autres : faire dir(obj) pour voir toutes les méthodes attachées à un objet. 1 2 3 4 5 6 7 8 9 10 11 12 13 class Complex: """ Defines a new complex type """ def __init__(self, x_init, y_init): self.x =x_init self.y =y_init def __add__(self, z): """ Addition in C """ return Complex(self.x+z.x, self.y+z.y) def __str__(self): return str(self.x)+"+"+str(self.y)+"i" z = Complex(3,4) z2 = Complex(-2,-2) print z+z2 L’exécution de ce code conduit à : 1 + 2i 5.6 Documention et interrogation Les textes entre triple guillemets """ ... """ définissent la documentation. Ainsi help(z) ainsi que help(Complex) donnent : Help on instance of Complex in module cmplx: class Complex | Defines a new complex type | | Methods defined here: | | __add__(self, z) | Addition in C | | __init__(self, x_init, y_init) | | __str__(self) On peut demander la documentation de tout objet, y compris une méthode. Ainsi help(z.__add__) donne : 90 Help on method __add__ in module cmplx: __add__(self, z) method of cmplx.Complex instance Addition in C L’opérateur dir() renseigne sur tous les attributs d’un objet, aussi bien ses méthodes que ses variables d’instance. Si z est un objet de classe Complex, dir(z) donne : [ ’ __add__ ’ , ’ __doc__ ’ , ’ __init__ ’ , ’ __module__ ’ , ’ __str__ ’ , ’x ’ , ’y ’] Dans cette liste, ’x’ et ’y’ sont les noms des variables d’instance de z, ’__init__’ est son constructeur, et ’__add__’ et ’__str__’ sont des méthodes surchargées. La variable ’__module__’ renseigne sur le module dans lequel est défini l’objet z. Si le programme est lancé directement depuis la console, elle vaudra ’__main__’. Si le programme est placé dans un module cmplx.py que l’on importe par import cmplx, elle vaudra ’cmplx’. La variable __doc__ contient le texte affiché par la commande help(z). Note : la classe complex (cf. section 2.1.1) fonctionne très bien ; il n’est pas nécessaire d’en définir une nouvelle. 5.6.1 Conventions de nommage Il est d’usage de réserver les doubles _ pour les méthodes liées à un opérateur, comme ’__init__’, ’__str__’ ou ’__add__’. Les variables d’instances ou les méthodes auront un nom commençant par _, comme par exemple _x ou _flag, si elles sont privées. Cela signifie qu’on conseille aux programmes utilisant la classe de ne pas utiliser directement ces variables ou méthodes. Les variables d’instances ou les méthodes publiques auront un nom commençant par une lettre minuscule. 5.7 Héritage Il est possible de créer un nouveau type objet en le faisant hériter d’un type existant (ou de plusieurs type existants : on parle alors d’héritage multiple). Toutes les méthodes du type parent sont disponibles, mais il est possible de les surcharger pour modifier leur comportement. Il convient d’invoquer le constructeur du type parent __init__() avant de réaliser les opérations d’initialisation du type descendant. 5.7.1 Appartenance à une classe La fonction isinstance(objet, classe) renvoie True si objet est de classe classe ou d’un descendant de classe Elle permet notamment de tester le type des paramètres reçus par une fonction. On peut aussi demander son type à un objet par l’opérateur type(). 91 >>> grg = { " Earth " : " harmless " } >>> type ( grg ) < type ’ dict ’> 5.8 Exemple : géométrie par les objets La classe Point définit un point par deux variables d’instances, x et y. Elle surcharge les opérateurs +, - et * pour donner à un Point les propriétés d’un élément d’un espace vectoriel 2 . La méthode dist calcule la distance euclidienne entre deux Points. Il n’y a pas de produit scalaire ni de produit vectoriel : il est calculé de manière manuelle dans les méthodes de Triangle qui en ont besoin. La classe Figure fournit le squelette de description d’une figure géométrique, et lui impose d’avoir un centre et une surface. Toutefois, cette classe générique n’effectue pas les calculs correspondants : ils sont confiés aux trois sous-classes, Circle, Koch et Triangle. Plusieurs paramètres sont testés, que ce soit par isinstance() ou en vérifiant que les conversions se passent bien dans un bloc try: . . . except:. En cas de problème, une exception est levée, ce qui met fin à l’exécution du programme si elle n’est pas traitée dans le cadre d’un bloc try: . . . except: englobant. 1 2 3 import numpy as np import matplotlib.pyplot as plt from matplotlib.backends.backend_pdf import PdfPages 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 class Point: """ Class Point :implements addition, substraction, and multiplication with a scalar """ def __init__(self, init_x, init_y): self.x =float(init_x) self.y =float(init_y) def __str__(self): return ’(%f, %f)’%(self.x, self.y) def __sub__(self, p): if not isinstance(p, Point): raise TypeError(’Target should be a Point’) else: return Point(self.x -p.x, self.y -p.y) def __add__(self, p): if not isinstance(p, Point): raise TypeError(’Operand should be a Point’) else: return Point(self.x +p.x, self.y +p.y) def __rmul__(self, v): """ use by ’v *p’ where p is a Point and v a scalar """ try: mult = float(v) return Point(mult*self.x, mult*self.y) except: raise TypeError(’Bad multiplier’) def dist2(self, p): """ make use of p1.dist2(p2) instead of (p1.dist(p2))**2 """ 2. Encore que l’opérateur - se définisse plus sainement comme associant un élément d’un espace vectoriel à deux éléments d’un espace affine. 92 31 32 33 34 35 36 if not isinstance(p, Point): raise TypeError(’Target should be a Point’) else: return (self.x-p.x)**2 +(self.y-p.y)**2 def dist(self, p): return self.dist2(p)**0.5 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 class Figure(): def __init__(self, init_center, init_radius): if isinstance(init_center, Point): self.center =init_center else: raise TypeError(’Center should be a Point’) if float(init_radius)>0: self.radius =float(init_radius) def __str__(self): return ’Center :’+str(self.center)+’, Radius :’+str(self.radius) def surface(self): return None def perimeter(self): return None def draw(self, axes): """ axes should be a subplot of a pyplot figure """ pass 55 56 57 58 59 60 61 62 63 64 65 66 67 68 class Circle(Figure): def __init__(self, init_center, init_radius): Figure.__init__(self, init_center, init_radius) def __str__(self): return ’Circle ’+Figure.__str__(self) def surface(self): return np.pi*self.radius**2 def perimeter(self): return np.pi*self.radius*2 def draw(self, axes): # Note that Circle and plt.Circle are completely different classes circ = plt.Circle((self.center.x, self.center.y), radius=self. radius, color=’b’) axes.add_patch(circ) 69 70 71 72 73 74 75 76 77 78 class Koch(Figure): def __init__(self, init_center, init_radius): Figure.__init__(self, init_center, init_radius) def __str__(self): return ’Koch snowflake ’+Figure.__str__(self) def surface(self): return 1.8*0.75*3**0.5*self.radius**2 def perimeter(self): return np.Inf 79 80 81 82 83 84 85 86 87 88 class Triangle(Figure): def __init__(self, A, B, C): if isinstance(A, Point) and isinstance(B, Point) and isinstance(C , Point): self.vertex =[A, B, C] AB = B-A AC = C-A product =AB.x*AC.y -AB.y*AC.x radius =0.5*A.dist(B)*B.dist(C)*C.dist(A)/product AM = 0.5/product *Point(AC.y*A.dist2(B)-AB.y*A.dist2(C), AB.x *A.dist2(C)-AC.x*A. 93 89 90 91 92 93 def 94 95 96 97 98 99 100 def 101 102 def 103 104 105 106 107 108 109 110 111 112 113 114 def dist2(B)) center =A + AM Figure.__init__(self, center, radius) else: raise TypeError(’Need three Point to define a Triangle’) __str__(self): s = ’Triangle (’ for p in self.vertex: s += str(p) s += ’)\n ’ s += Figure.__str__(self) return s perimeter(self): return self.vertex[0].dist(self.vertex[1]) +self.vertex[1].dist( self.vertex[2]) +self. vertex[2].dist(self. vertex[0]) surface(self): AB = self.vertex[1]-self.vertex[0] AC = self.vertex[2]-self.vertex[0] return 0.5*(AB.x*AC.y -AB.y*AC.x) draw(self, axes): """ Draws the Triangle in red and its circumcenter in black """ tri_ccenter =plt.Circle((self.center.x, self.center.y), radius= self.radius*0.05, color=’ k’) vertices =np.array([[self.vertex[0].x, self.vertex[0].y], [self.vertex[1].x, self.vertex[1].y], [self.vertex[2].x, self.vertex[2].y]]) tri = plt.Polygon(vertices, color=’r’) axes.add_patch(tri) axes.add_patch(tri_ccenter) 115 116 117 118 119 120 f = Figure(Point(0,2), 3) print f print "Perimeter of f:", f.perimeter() print "Surface of f:", f.surface(), "\n" 121 122 123 124 125 c = Circle(Point(0,2), 3) print c print "Perimeter of c:", c.perimeter() print "Surface of c:", c.surface(), "\n" 126 127 128 129 130 t1 = Triangle(Point(0,0), Point(3,0), Point(0,4)) print t1 print "Perimeter of t1:", t1.perimeter() print "Surface of t1:", t1.surface(), "\n" 131 132 133 134 135 t2 = Triangle(Point(-4,0), Point(-2,0), Point(-3,3**0.5)) print t2 print "Perimeter of t2:", t2.perimeter() print "Surface of t2:", t2.surface(), "\n" 136 137 138 139 140 141 142 # First iteration of the Koch fractal is an equilateral triangle of # surface 1 k = Koch(Point(0,0), 2*3**-0.75) print k print "Perimeter of k:", k.perimeter() print "Surface of k:", k.surface() 143 144 # Draws the Circle and the two Triangle on a pylab figure 94 145 146 147 148 149 150 151 152 153 154 155 fig = plt.figure(1) ax = fig.add_subplot(1, 1, 1) plt.xlim(-5, 6) plt.ylim(-2, 6) c.draw(ax) t1.draw(ax) t2.draw(ax) pp = PdfPages("../img/ex_figures.pdf") pp.savefig() pp.close() #fig.show() L’exécution de ce programme donne : Center : (0.000000, 2.000000), Radius : 3.0 Perimeter of f: None Surface of f: None Circle Center : (0.000000, 2.000000), Radius : 3.0 Perimeter of c: 18.8495559215 Surface of c: 28.2743338823 Triangle ((0.000000, 0.000000)(3.000000, 0.000000)(0.000000, 4.000000)) Center : (1.500000, 2.000000), Radius : 2.5 Perimeter of t1: 12.0 Surface of t1: 6.0 Triangle ((-4.000000, 0.000000)(-2.000000, 0.000000)(-3.000000, 1.732051)) Center : (-3.000000, 0.577350), Radius : 1.15470053838 Perimeter of t2: 6.0 Surface of t2: 1.73205080757 Koch snowflake Center : (0.000000, 0.000000), Radius : 0.877382675302 Perimeter of k: inf Surface of k: 1.8 6 5 4 3 2 1 0 1 2 4 2 0 2 4 6 Figure 5.1 – Affichage de trois objets sur pyplot 95 6 Pour aller plus loin Sommaire 6.1 6.2 6.3 6.4 6.5 Analyse de texte par expressions régulières Interrogation du web et format HTML Interaction avec le système d’exploitation Utilisation de fichiers par pickle Bases de données 6.5.1 Connection 6.5.2 Requêtes SQL 6.6 Passage de Python 2.x à Python 3.x 96 97 98 98 99 99 99 100 A u-delà des fonctionnalités présentées durant ce stage, Python offre de nombreuses possibilités. Les expressions régulières et l’interrogation du web étendent ce qui a été fait avec les chaînes de caractères et les fichiers texte. La bibliothèque pickle permet de sauvegarder aisément toutes les informations utilisées par un programme Python. La bibliothèque pyodbc permet d’écrire un client pour une base de données. Il en existe bien d’autres. pygame permet d’écrire des jeux (ou plus généralement d’interagir en temps réel avec l’utilisateur). pyopengl permet de créer des scènes en 3D (connaître le C aide à comprendre la logique de sa syntaxe). socket permet de créer une connection entre deux programmes sur deux machines distantes via le protocole TCP/IP, fournissant la base d’une architecture client/serveur. ctypes et scipy.weave permettent de lier C et Python pour accélerer les fonctions les plus utilisées et les plus gourmandes en temps de calcul. cv est le port sous Python d’OpenCV : il permet l’acquisition par caméra et le traitement d’image ; il s’interface aisément avec numpy et PIL. unittest permet de formaliser les batteries de tests unitaires pour garantir 1 que le programme fonctionne ou au moins que son évolution n’entraîne pas de régression. 6.1 Analyse de texte par expressions régulières Le module re implémente en Python les expressions régulières, qui analysent une chaîne de caractère pour tester si celle-ci contient un certain motif. 1. Autant que possible : de façon générale, prouver le bon fonctionnement d’un programme est indécidable. 96 compile(str) transforme une chaîne de caractères en un objet expression régulière, qui est spécialisé dans la recherche de ce motif particulier (et donc plus rapide à exécuter). search(str) ou search(motif, str) : vrai si la chaîne contient le motif compilé ou le motif passé en paramètre. Renvoie un objet de type MatchObject. motif.match(str) ou search(motif, str) : vrai si la chaîne correspond dans son intégralité au motif compilé ou le motif passé en paramètre. Renvoie un objet de type MatchObject. MatchObject.group(k) : Renvoie la chaîne de caractères correspondant aux k e paranthèses de l’expression régulière. 1 import sys, re 2 3 4 5 6 7 8 9 6.2 test42 =re.compile(".*4(.*)2.*") while True: s = sys.stdin.readline() mo = test42.match(s) if mo: print "Between 4 and 2 lies ", print mo.group(1) Interrogation du web et format HTML Le module urllib permet d’accéder à des pages web. La fonction urlopen(url) prend comme paramètre la chaîne de caractère contenant l’url demandée (y compris “http://”) et renvoie la page web correspondante. Il convient de la lire comme un ensemble de lignes. 1 2 import urllib import sys 3 4 5 6 7 8 9 10 11 12 if __name__ ==’__main__’: if len(sys.argv)>1: if sys.argv[1][:7] ==’http://’: url = sys.argv[1] else: url = ’http://’+sys.argv[1] page = urllib.urlopen(url) for line in page: print line La page web est livrée avec les balises HTML. Pour exploiter la structure du code HTML, il faut utiliser le module HTMLParser. Si on préfère analyser la page web comme un texte quelconque, on peut se limiter aux fonctionnalités de la classe str ou de la bibliothèque re. Le module HTMLParser définit une classe HTMLParser dont le rôle est de traiter les informations contenues dans le fichier HTML. Les méthodes adéquates du parser sont appelées lorsque des balises, des données, ou d’autres éléments HTML sont rencontrés. Par défaut, le HTMLParser ne fait rien dans ces cas : il convient de définir une classe-fille de HTMLParser qui s’en charge. 1 2 import urllib from HTMLParser import HTMLParser 3 97 4 5 6 7 8 9 10 11 12 13 class MyHTMLParser(HTMLParser): def handle_starttag(self, tag, attrs): print "Debut de :", tag def handle_endtag(self, tag): print "Fin de :", tag def handle_data(self, data): if data[-1] !="\n": print "Donnees :", data[0:] else: print "Donnees :", data[0:-1] 14 15 16 17 18 19 6.3 parser =MyHTMLParser() url = ’http://www.google.com’ page = urllib.urlopen(url) for line in page: parser.feed(line) Interaction avec le système d’exploitation Le module os donne accès au système d’exploitation. system(str) : exécute la commande unix/linux définie par la chaîne de caractères str et renvoie son code de sortie. popen(str) : exécute la commande linux str et renvoie sa sortie standard. On peut alors parcourir celle-ci par une boucle for. 1 2 3 4 import os s = os.popen("ls") for line in s : print line , Il est désormais conseillé d’utiliser le module subprocess plutôt que os, plus proche de la structure du système d’exploitation réel, mais à la syntaxe plus lourde 2 . 6.4 Utilisation de fichiers par pickle On utilise le module pickle pour réaliser la lecture et l’écriture de données typées en Python. Cela permet d’écrire des données et lire des données en conservant leur type (nombre, liste, chaîne de caractère, table de hachage, array de numpy, . . . ). Pour écrire : >>> >>> >>> >>> >>> import pickle x=2 f = open ( " fichier " , ’w ’) pickle . dump (x , f ) # é crit x f . close () Pour relire : 2. . . . et que l’on ne comprend qu’une fois que l’on sait comment dont gérées les entréessorties et les redirections par le système. 98 >>> >>> >>> >>> 2 f = open ( " fichier " , ’r ’) x = pickle . load ( f ) # lit x f . close () print x 6.5 Bases de données Un système de gestion de bases de données (SGBD) est un programme chargé de stocker de l’information structurée sur un serveur, ainsi que de l’interroger et la mettre à jour. Les SGBD libres les plus courants sont PostgreSQL, SQLite, MySQL ou son fork récent MariaDB ; les SGBD commerciaux les plus courants sont Access et Oracle. Tous sont accessibles depuis la bibliothèque pyodbc (Python Open DataBase Connectivity). Les données sont structurées sous forme de tables, dont les colonnes sont fortement typées. Chaque ligne correspond à un enregistrement dans la table (une personne, un cours, une facture, . . . ). Le serveur est interrogé en 3 SQL (Structured Query Langage). 6.5.1 Connection La première opération est de se connecter au serveur, ce qui se fait par la fonction connect(). Il faut renseigner le driver à utiliser, c’est-à-dire le type de SGBD, ainsi que la machine sur laquelle il se trouve, et l’identité du client. On récupère ensuite un curseur sur cette connexion qui permettra d’envoyer des requêtes SQL au serveur. Une connection à un serveur MySQL installé sur le serveur web de l’école pourrait être : cnxn = connect ( driver = ’{ MySQL } ’ , server = ’ www . ensta - bretagne . fr ’ , database = ’ test ’ , uid = ’ osswald ’ , pwd = ’ posswd ’) cursor = cnxn . cursor () 6.5.2 Requêtes SQL Une interrogation SQL commence par SELECT et renvoie une ou plusieurs lignes composées d’une ou plusieurs colonnes. SELECT colonne(s) FROM table(s) [WHERE condition(s)] [[GROUP BY colonne(s)] [HAVING condition(s)]] [ORDER BY colonne(s)]; La requête est transmise par la méthode execute() du curseur. L’information renvoyée par le SGBD est disponible au travers de ce même curseur. sqlreq = " SELECT etud_nom , note FROM etudiants WHERE spe = ’ MP ’ and note > 8 . 5 ORDER BY note DEC ; " cursor . execute ( sqlreq ) 3. Chaque SGBD “parle” son propre idiome de SQL, se référer à la documentation du SGBD cible pour écrire les requêtes. 99 Il est possible de récupérer l’information ligne par ligne par fetchone(). La ligne est de type Row ; ses colonnes peuvent être extraites par leur numéro de colonne, comme un tuple, mais aussi par le nom de la colonne dans la requête. while True : row = cursor . fetchone () if not row : break print ’ Nom : ’ , row . etud_nom , ’\ tNote : ’ , row [ 1 ] La méthode fetchall permet de récupérer toute l’information renvoyée dans une unique structure, que l’on peut parcourir par for. sqlreq = " SELECT etud_nom , note FROM etudiants WHERE spe = ’ MP ’ and note > 8 . 5 ORDER BY note DEC ; " cursor . execute ( sqlreq ) rows = cursor . fetchall () for row in rows : print ’ Nom : ’ , row . etud_nom , ’\ tNote : ’ , row [ 1 ] Le curseur lui-même peut être utilisé comme un itérateur : sqlreq = " SELECT etud_nom , note FROM etudiants WHERE spe = ’ MP ’ and note > 8 . 5 ORDER BY note DEC ; " cursor . execute ( sqlreq ) for row in cursor : print ’ Nom : ’ , row . etud_nom , ’\ tNote : ’ , row [ 1 ] Une requête peut aussi être une modification de la table. La méthode commit() rend les modifications définitives sur le serveur. sqlreq = " INSERT INTO etudiants VALUES ( ’ Kasparov ’, ’ MP ’, ’ Info ’, 15 . 6 ) ; " cursor . execute ( sqlreq ) cnxn . commit () 6.6 Passage de Python 2.x à Python 3.x Mark Summerfield [Sum09] a réalisé un résumé efficace des différences entre les dernières versions de Python. 100 Moving from Python 2 to Python 3 Introduction This document is aimed at Python 2 programmers wishing to start devel- oping using Python 3. The document lists those objects and idioms that have changed between the versions, showing how to change from Python 2-style to Python 3.1-style. It also lists some of the most useful new features in Python 3.1. Note that if you want to convert an existing program or module, the place to start is with the 2to3 tool (docs.python.org/library/2to3.html). For a complete introduction to the Python 3 language (covering both 3.0 and 3.1, and vastly more than there’s room for here), see Programming in Python 3 (Second Edition) by Mark Summerfield, ISBN 0321680561 (www.qtrac.eu/py3book.html). Printing and Executing Strings and String Formatting Python 3 strings are Unicode; unicode() is gone Python 2 Python 3.1 New functions print(), exec(); execfile() is gone Python 2 Python 3.1 s = unicode(x) s = str(x) print a, b, c print(a, b, c) s = u"\u20AC" s = "\u20AC" print "%03d" % 7 print("{:03d}".format(7)) s = ur"\w" s = r"\w" print x, print(x, end=" ") String % operator is deprecated; use str.format() "%d %s" % (i, s) "%(i)d %(s)s" % ( {'i':i, 's':s}) print>>sys.stderr, x print(x, file=sys.stderr) "{} {}".format(i, s) exec code exec(code) "{0} {1}".format(i, s) exec code in globals exec(code, globals) "{i} {s}".format(i=i, s=s) exec code in ( globals, locals) exec(code, globals, locals) execfile(file) with open(file) as fh: exec(fh.read()) "{0[i]} {0[s]}".format( {'i':i, 's':s}) "{i} {s}".format( **{'i':i, 's':s}) "{i} {s}".format( **locals()) "%s-%s" % ("X", "X") "{0}-{0}".format("X") "%(i)d %(s)s" % ( locals()) "{:.2f}".format(3.142) "%.2f" % 3.142 "{0:.2f}".format(3.142) "{π:.2f}".format(π=3.142) "%.4s" % "Prime" "{:.4}".format("Prime") "{%d%%}" % 20 "{{{}%}}".format(20) "%0*d" % (3, 7) "{:0{}}".format(7, 3) Representational Form Backticks are gone; use repr() or str.format() Python 2 Python 3.1 s = `x` Division doesn’t truncate; long() is gone; octal literals must start with 0o (zero-oh) Python 2 Python 3.1 x = 5 / 2.0 # x==2.5 x = 5 / 2 x = 5 / 2 x = 5 // 2 # x==2 # x==2 # x==2.5 i = 2147483648L i = 2147483648 j = long(i * 2) x = 0123 # x==83 j = int(i * 2) x = 0o123 # x==83 Iterators New next(); iterators must have __next__() Python 2 Python 3.1 s = repr(x) x = iterator.next() s = "{!r}".format(x) class Iterator: class Iterator: def __init__(self): def __init__(self): self.i = -1 self.i = -1 def next(self): def __next__(self): self.i += 1 self.i += 1 return self.i return self.i s = "{0!r}".format(x) s = "{z!r}".format(z=x) Force ASCII representation with ascii() s = `x` Numbers s = ascii(x) s = "{!a}".format(x) x = next(iterator) fn.func_defaults fn.__defaults__ An operator, an exception, a constant, some types, several global functions, several dictionary methods, and some itertools functions are gone Python 2 Python 3.1 fn.func_dict fn.__dict__ fn.func_doc fn.__doc__ fn.func_globals fn.__globals__ fn.func_name fn.__name__ if a <> b: obj.method.im_func obj.method.__func__ obj.method.im_self obj.method.__self__ Removals and Replacements if a != b: fn(*args) apply(fn, args) apply(fn, args, kwargs) fn(*args, **kwargs) obj.method.im_class obj.method.__class__ if isinstance(x, basestring): string.letters string.ascii_letters string.lowercase string.ascii_lowercase string.uppercase string.ascii_uppercase threading.Lock. \ acquire_lock() threading.Lock. \ acquire() threading.Lock. \ release_lock() threading.Lock. \ release() class Thing: def __init__( self, x): self.x = x def __nonzero__( self): return \ bool(self.x) class Thing: def __init__( self, x): self.x = x def __bool__(self): return bool(self.x) if isinstance(x, str): x = memoryview(y) # this is similar if hasattr(x, "__call__"): x = buffer(y) if callable(x): fh = file(fname, mode) fh = open(fname, mode) if d.has_key(k): if k in d: for k, v in \ d.iteritems(): for k, v in d.items(): for k in d.iterkeys(): for k in d.keys(): for k in d: for v in \ d.itervalues(): for v in d.values(): for line in \ file.xreadlines(): for line in file: x = input(msg) x = eval(input(msg)) intern(s) sys.intern(s) f = itertools.ifilter( f = filter(fn, seq) fn, seq) m = itertools.imap( fn, seq) m = map(fn, seq) z = itertools.izip( seq1, seq2) z = zip(seq1, seq2) dir = os.getcwdu() dir = os.getcwd() s = raw_input(msg) s = input(msg) r = reduce(fn, seq) r = functools.reduce( fn, seq) reload(module) imp.reload(module) class MyErr( StandardError): class MyErr( Exception): sys.maxint sys.maxsize for i in xrange(n): for i in range(n): Exceptions Catching exception objects requires the as keyword; raising exceptions with arguments requires parentheses; strings cannot be used as exceptions Python 2 Python 3.1 try: process() except ValueError, \ err: print err try: process() except ValueError \ as err: print(err) try: try: process() process() except (MyErr1, except (MyErr1, MyErr2), err: MyErr2) as err: print err print(err) raise MyErr, msg raise MyErr(msg) raise MyErr, msg, tb raise MyErr(msg). \ with_traceback(tb) Implement __bool__() instead of __nonzero__() to return a custom class’s truth value Python 2 Python 3.1 raise "Error" raise Exception( "Error") generator.throw( MyErr, msg) generator.throw( MyErr(msg)) fn.func_closure fn.__closure__ fn.func_code fn.__code__ generator.throw( "Error") generator.throw( Exception("Error")) Renamed Attributes and Methods Renamed Modules Python 3.1Idioms Data read from a URL, e.g., using urllib.request. urlopen() is returned as a bytes object; use bytes.decode(encoding) to convert it to a string. The bsddb (Berkeley DB library) is gone—but is avaliable from pypi.python.org/pypi/bsddb3. See PEP 3108 (www.python.org/dev/peps/pep-3108) for module renaming details Python 2 Python 3.1 Tuples need parentheses in comprehensions; metaclasses are set with the metaclass keyword; import the pickle and string I/O modules directly; lambda doesn’t unpack tuples; set literals are supported (the empty set is set(); {} is an empty dict); sorting is fastest using a key function; super() is better; type-testing is more robust with isinstance(); use True and False rather than 1 and 0 Python 2 Python 3.1 import anydbm import whichdb import dbm import BaseHTTPServer import \ SimpleHTTPServer import http.server import CGIHTTPServer import __builtin__ import builtins import commands import subprocess import ConfigParser import configparser import Cookie import http.cookies import cookielib import http.cookiejar import copy_reg import copyreg import dbm import dbm.ndbm import DocXMLRPCServer import \ SimpleXMLRPCServer import xmlrpc.server import dumbdbm import dbm.dumb import gdbm import dbm.gnu import httplib import http.client import Queue import queue import repr import reprlib import robotparser import SocketServer import \ test.test_support import Tkinter urllib.robotparser class A( metaclass=MyMeta): pass class B(MyBase, metaclass=MyMeta): pass try: import cPickle \ as pickle except ImportError: import pickle import pickle try: import cStringIO \ as StringIO except ImportError: import StringIO import io fn = lambda (a,): \ abs(a) fn = lambda (a, b): \ a + b S = set((2, 4, 6)) fn = lambda t: \ abs(t[0]) fn = lambda a: abs(a) fn = lambda t: \ t[0] + t[1] fn = lambda a, b: a + b S = {2, 4, 6} import socketserver S = set([2, 4, 6]) import test.support L = list(seq) L.sort() L = sorted(seq) words.sort( lambda x, y: cmp(x.lower(), y.lower())) words.sort( key=lambda x: x.lower()) urllib.request, \ class B(A): def __init__(self): super(B, self). \ __init__() class B(A): def __init__(self): super(). \ __init__() urllib.error if type(x) == X: import tkinter urllib.request, \ urllib.parse, \ urllib.error import \ import urllib2 L = [x for x in (3, 6)] class A: __metaclass__ = \ MyMeta class B(MyBase): __metaclass__ = \ MyMeta import \ import \ import urllib L = [x for x in 3, 6] import urlparse import urllib.parse import xmlrpclib import xmlrpc.client if type(x) is X: while 1: process() if isinstance(x, X): while True: process() New in Python 3.1 General Notes Dictionary and set comprehensions; * unpacking; binary literals; bytes and bytearray types; bz2.BZ2File and gzip.GzipFile are context managers; collections.Counter dictionary type; collections.OrderedDict insertion-ordered dictionary type; decimal.Decimals can be created from floats Python 2 Python 3.1 Python 3 often returns iterables where Python 2 returned lists. This is usually fine, but if a list is really needed, use the list() factory function. For example, given dictionary, d, list(d.keys()) returns its keys as a list. Affected functions and methods include dict.items(), dict.keys(), dict.values(), filter(), map(), range(), and zip(). Most of the types module’s types (such as types. LongType) have gone. Use the factory function instead. For example, replace if isinstance(x, types.IntType) with if isinstance(x, int). Comparisons are strict—x < y will work for compatible types (e.g., x and y are both numbers or both strings); otherwise raises a TypeError. Some doctests involving floating point numbers might break because Python 3.1 uses David Gay’s algorithm to show the shortest representation that preserves the number’s value. For example, 1.1 in Python 2 and 3.0 is displayed as 1.1000000000000001, and in Python 3.1 as 1.1. d = {} d = {x: x**3 for x in range(5): for x in range(5)} d[x] = x**3 S = set( S = {x for x in seq} [x for x in seq]) Python 3.1 a, *b = (1, 2, 3) # a==1; b==[2, 3] # a==[1, 2]; b==3 *a, b = (1, 2, 3) a, *b, c = (1, 2, 3, 4) # a==1; b==[2, 3]; c==4 x = 0b1001001 s = bin(97) y = int(s, 2) # x==73 # s=='0b1100001' # y==97 u = "The " # or: u = "The \u20ac" # or: u = "The \N{euro sign}" v = u.encode("utf8") # v==b'The \xe2\x82\xac' w = v.decode("utf8") # w=='The ' x = bytes.fromhex("54 68 65 20 E2 82 AC") # x==b'The \xe2\x82\xac' y = x.decode("utf8") # y=='The ' z = bytearray(y) z[-3:] = b"$" # z==bytearray(b'The $') with bz2.BZ2File(filename) as fh: data = fh.read() counts = collections.Counter("alphabetical") # counts.most_common(2)==[('a', 3), ('l', 2)] d = collections.OrderedDict( (("x", 1), ("k", 2), ("q", 3))) # list(d.keys())==['x', 'k', 'q'] dec = decimal.Decimal.from_float(3.75) # dec==Decimal('3.75') Special Methods The slice methods (__delslice()__, __getslice()__, __setslice__) are gone; instead __delitem()__, __getitem()__, and __setitem__ are called with a slice object. The methods __hex__() and __oct__() are gone; use hex() and oct(). To provide an integer, implement __index__(). String Format Specifications strings have one or more replacement fields of form: {Name!Conv:Spec}. Name identifies the object to format. Optional !Conv is: !a (ASCII repr() format), !r (repr() format), or !s (string format). Optional :Spec is of form: str.format() : Fill Align Sign # 0 Width , .Prec Type is any character except }. Align is: < (left), > (right), ^ (center), or = (pad between sign and number). Sign is: + (force), - (- if needed), or “ ” (space or -). # prefixes ints with 0b, 0o, or 0x. 0 means 0-pad numbers. Width is the minimum width. The , means use grouping commas. .Prec is the maximum width for strs and number of decimal places for floats. Type is: % (percent), b (binary), d (decimal), e or E (exponential), f (float) g or G (general float) n (localized) o (octal), x or X (hex). Everything is optional, except that Fill requires Align. Fill "{:*=+10.1f}".format(12345.67) "{:*>+10.1f}".format(12345.67) "{:+010.1f}".format(12345.67) "{:,.2f}".format(12345.678) # # # # '+**12345.7' '**+12345.7' '+0012345.7' '12,345.68' An informIT.com publication by Mark Summerfield. #3 Copyright Qtrac Ltd. 2009. License: Creative Commons Attribution-Share Alike 3.0 U.S. Index Symbols * . . . . . . . . . . . . . . . . . . . . . . . . . . 18, 23, 30 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 % . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 _ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 2to3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 3D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 A ABC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 abc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 abs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 addition . . . . . . . . . . . . . . . . . . . . . . . . . . 30 affectation . . . . . . . . . . . . . . . . . . . . . . . . 25 affichage . . . . . . . . . . . . . . . . . . . . . . . . . . 60 algèbre . . . . . . . . . . . . . . . . . . . . . . . . 22, 30 linéaire . . . . . . . . . . . . . . . . . . . . . . . 31 matricielle . . . . . . . . . . . . . . . . . . . . 86 and . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 animation . . . . . . . . . . . . . . . . . . . . . . . . . 46 anti-slash . . . . . . . . . . . . . . . . . . . . . . . . . 59 apostrophe . . . . . . . . . . . . . . . . . . . . . . . . 59 append . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 arange . . . . . . . . . . . . . . . . . . . . . . . . 26, 27 arguments . . . . . . . . . . . . . . . . . . . . . . . . 58 argv . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 arithmétique . . . . . . . . . . . . . . . . . . 15, 21 array . . . . . . . . . . . . . . . . . . . . . . . . . . 23, 25 arrondi . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 as. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .17 B base de données . . . . . . . . . . . . . . . . . . . 99 bibliothèque . . . . . . . . . . . . . . . . . . . . . . 17 bool . . . . . . . . . . . . . . . . . . . . . . . . . . . 12, 54 booléen . . . . . . . . . 12, 14, 17, 18, 53, 54 boucle . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 break . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 C C..................................6 calculatrice . . . . . . . . . . . . . . . . . . . . . . . 11 caractère . . . . . . . . . . . . . . . . . . . . . . . . . . 59 casse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 classe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 close . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 cmp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 commentaires . . . . . . . . . . . . . . . . . . . . . 51 comparaison. . . . . . . . . . . . . . . . . . .17, 53 compilateur . . . . . . . . . . . . . . . . . . . . . . . . 6 complex . . . . . . . . . . . . . . . . . . . 12, 14, 19 complexité . . . . . . . . . . . . . . . . . . . . 61, 67 dans le pire des cas . . . . . . . . . . . 62 moyenne . . . . . . . . . . . . . . . . . . . . . . 62 concaténer . . . . . . . . . . . . . . . . . . . . 51, 79 concatenate . . . . . . . . . . . . . . . . . . . . . . . 28 condition d’arrêt . . . . . . . . . . . . . . . . . . 57 connect . . . . . . . . . . . . . . . . . . . . . . . . . . . 99 constructeur . . . . . . . . . . . . . . . . . . . . . . 87 continue . . . . . . . . . . . . . . . . . . . . . . . . . . 54 copie . . . . . . . . . . . . . . . . . . . . . . . . . . 26, 52 copy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 couleur . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 cursor . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99 D découper . . . . . . . . . . . . . . . . . . . . . . . . . . 59 del . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 dérivation . . . . . . . . . . . . . . . . . . . . . . . . . 84 dessin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80 105 det . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 développement limité . . . . . . . . . . . . . 85 diag . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 dict . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 dir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 division . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 dot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 double . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 dsolve. . . . . . . . . . . . . . . . . . . . . . . . . . . . .86 help . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 héritage . . . . . . . . . . . . . . . . . . . 76, 87, 91 HTML . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 HTMLParser . . . . . . . . . . . . . . . . . . . . . 97 I if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 image . . . . . . . . . . . . . . . . . . . . . . . . . 75, 76 import . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 in . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51, 53 indice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 liste . . . . . . . . . . . . . . . . . . . . . . . . . . 27 négatif . . . . . . . . . . . . . . . . . . . . . . . . 26 infini. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .12 insert . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 instance . . . . . . . . . . . . . . . . . . . . . . . . . . 87 int. . . . . . . . . . . . . . . . . . . . . . . . .12, 19, 69 integrate . . . . . . . . . . . . . . . . . . . . . . . . . . 85 interpréteur . . . . . . . . . . . . . . . . . . . . . . . . 6 interactif . . . . . . . . . . . . . . . . . . . . 8, 9 inv. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .31 isinstance . . . . . . . . . . . . . . . . . . . . . . . . . 91 itérateur . . . . . . . . . . . . . . . . . . . . . 54, 100 E échappement . . . . . . . . . . . . . . . . . . . . . . 59 eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 éditeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 égalité . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 eig . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 elif . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 else . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 emacs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 empty . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 équation algébrique . . . . . . . . . . . . . . . . . . . . 86 différentielle . . . . . . . . . . . . . . . 72, 86 résolution . . . . . . . . . . . . . . . . . . . . . 70 J except . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 Java . . . . . . . ........................ 6 exception . . . . . . . . . . . . . . . . . . . . . . . . . . 6 extend . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 L langage F compilé . . . . . . . . . . . . . . . . . . . . . . . . 6 False . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 interprété . . . . . . . . . . . . . . . . . . . . . . 6 fichier. . . . . . . . . . . . . . . . . . . . . . . . . . . . .74 interprété précompilé . . . . . . . . . . 7 file . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74, 76 len . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22, 51 filtre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 ligne de commande . . . . . . . . . . . . . . . . . 9 flatten . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 float . . . . . . . . . . . . . . . . . . . . . . . . . . 12, 19 limite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84 for . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54, 98 linspace . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 format . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 list. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .25 liste . . . . . . . . . . . . . . . . . . . . . . . 21, 51, 55 fraction vide . . . . . . . . . . . . . . . . . . . . . . . . . . 52 rationelle . . . . . . . . . . . . . . . . . . . . . 83 from . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 logiciel libre . . . . . . . . . . . . . . . . . . . . . . . . 5 long . . . . . . . . . . . . . . . . . . . . . . . . . . . 12, 13 G M gedit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 math . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 global . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 glue logicielle . . . . . . . . . . . . . . . . . . . . . . 6 matplotlib . . . . . . . . . . . . . . . . . 33, 45, 75 guillemet . . . . . . . . . . . . . . . . . . . . . . . . . 59 matrice . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 matrix . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 max . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 H mémoire . . . . . . . . . . . . . . . . . . . 12, 52, 89 hachage code . . . . . . . . . . . . . . . . . . . . . . . . . . 52 min . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 table . . . . . . . . . . . . . . . . . . . . . . . . . 52 module . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 haut niveau . . . . . . . . . . . . . . . . . . . . . . . . 6 Monty Python . . . . . . . . . . . . . . . . . . . . . 5 106 R multiplication matricielle . . . . . . . . . . . . . . . . 23, 31 r . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 terme à terme . . . . . . . . . . . . . . . . 30 raise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 mutable. . . . . . . . . . . . . . . . . . . . . . . . . . .52 ramasse-miette . . . . . . . . . . . . . . . . . 6, 89 randint . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 N random . . . . . . . . . . . . . . . . . . . . . . . 30, 69 NaN . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 range . . . . . . . . . . . . . . . . . . . . . . . . . 22, 54 nombre . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 rank . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 noms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 Rational . . . . . . . . . . . . . . . . . . . . . . . . . . 82 None . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 ravel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 not . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 re . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 numpy . . . . . . . . . . . . . . . . . 17, 18, 25, 69 read . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 récursivité . . . . . . . . . . . . . . . . . . . . . . . . 57 O objet . . . . . . . . . . . . . . . . . . . . . . . . . . . 6, 87 regexp . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 odeint . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 remove. . . . . . . . . . . . . . . . . . . . . . . . . . . .51 ones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 repr. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .13 oo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82 reshape . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 open . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 retour à la ligne . . . . . . . . . . . . . . . . . . . 59 OpenCV . . . . . . . . . . . . . . . . . . . . . . . . . . 75 return . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 opérateur . . . . . . . . . . . . . . 15, 30, 90, 91 rotation . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 or. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .53 round . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 os. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .98 S scipy . . . . . . . . . . . . . . . . . . . . . . . . . . 32, 33 série . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 series . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 SGBD . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99 solve . . . . . . . . . . . . . . . . . . . . . . . . . . 31, 86 sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 split . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 spyder . . . . . . . . . . . . . . . . . . . . . . 7, 15, 18 SQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99 str. . . . . . . . . . . . . . . . . . . . . . . . .13, 58, 59 string . . . . . . . . . . . . . . . . . . . . . . . . . 59, 96 subprocess . . . . . . . . . . . . . . . . . . . . . . . . 98 summation. . . . . . . . . . . . . . . . . . . . . . . .85 suppression . . . . . . . . . . . . . . . . . . . . . . . 51 surcharge d’opérateur . . . . . . . . . . . . . 90 symbole. . . . . . . . . . . . . . . . . . . . . . . . . . .82 sympy . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82 sys. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .58 system . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 P paquet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 paramètre . . . . . . . . . . . . . . . . . . . . . 55, 57 pass . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 Perl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 pickle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 PIL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 pluma . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 pop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 popen. . . . . . . . . . . . . . . . . . . . . . . . . . . . .98 primitive . . . . . . . . . . . . . . . . . . . . . . . . . . 85 print . . . . . . . . . . . . . . . . . . . . . . 11, 13, 60 privée . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 produit scalaire . . . . . . . . . . . . . . . . . . . . . . . 31 publique . . . . . . . . . . . . . . . . . . . . . . . . . . 91 PyDev . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 pylab . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 pyodbc . . . . . . . . . . . . . . . . . . . . . . . . . . . 99 pyplot . . . . . . . . . . . . . . . . . . . . . . . . . 33, 92 T Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 3.* . . . . . . . . . 5, 15, 19, 53, 60, 100 tableau . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 Python(x,y) . . . . . . . . . . . . . . . . . . . . . 6, 7 tabulation . . . . . . . . . . . . . . . . . . . . . . . . 59 temps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 time . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 Q quad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 transformation . . . . . . . . . . . . . . . . . . . . 77 quadrature numérique. . . . . . . . . . . . .71 transtypage . . . . . . . . . . . . 14, 27, 58, 89 quicksort. . . . . . . . . . . . . . . . . . . . . . . . . .66 tri. . . . . . . . . . . . . . . . . . . . . . . . . . . . .60, 67 107 bulles . . . . . . . . . . . . . . . . . . . . . . . . . 62 insertion . . . . . . . . . . . . . . . . . . 63, 64 segmentation . . . . . . . . . . . . . . . . . 66 sélection . . . . . . . . . . . . . . . . . . 60, 61 Shell . . . . . . . . . . . . . . . . . . . . . . 65, 66 trigonométrie . . . . . . . . . . . . . . . . . . . . . 19 hyperbolique . . . . . . . . . . . . . . . . . . 20 True . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 try . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 tuple . . . . . . . . . . . . . . . . . . . . . . . . . . 52, 56 typage dynamique . . . . . . . . . . . . . . . . . 6, 12 fort . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 type . . . . . . . . . . . . . . . . . . . . . . . 14, 18, 91 U url . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 urllib . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 UTF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 V valeur de retour . . . . . . . . . . . . . . . . . . . . . 56 par défaut . . . . . . . . . . . . . . . . . . . . 57 Van Rossum . . . . . . . . . . . . . . . . . . . . . . . 5 var . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 variable . . . . . . . . . . . . . . . . . . . . . . . 13, 16 globale . . . . . . . . . . . . . . . . . . . . 55, 56 locale . . . . . . . . . . . . . . . . . . . . . . . . . 55 vdot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 vecteur . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 version . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 W write. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .74 Z zeros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 108 Bibliographie [Bre12] Bressert, Eli: SciPy and NumPy. O’Reilly, 2012 [BS09] Bicquelet-Salaün, Jean-Louis: PIL Frequently-Asked Questions. http://jlbicquelet.free.fr/scripts/python/pil/ pil.php. Version: 2009 [CBCC11] Casamayou-Boucau, Alexandre ; Cauvin, Pascal ; Connan, Guillaume: Programmation en Python pour les mathématiques. Dunod, 2011 [CLRS94] Cormen, Thomas H. ; Leiserson, Charles E. ; Rivest, Ronald L. ; Stein, Cliffort: Introduction à l’algorithmique. 2. Dunod, 1994 [Kiu10] Kiusalaas, Jaan: Numerical methods in engineering with python. 2nd. Cambridge, 2010 [Knu68] Knuth, Donald E.: The art of computer programming. Bd. 1. Addison-Wesley, 1968 [Knu73] Knuth, Donald E.: The art of computer programming. Bd. 3. Addison-Wesley, 1973 [Lan11] Langtangen, Hans P.: A Primer on Scientific Programming with Python. Springer, 2011 [Lut09] Lutz, Mark: Learning Python. O’Reilly, 2009 [Lut10] Lutz, Mark: Programming Python. O’Reilly, 2010 [Pet02] Peters, Tim: Timsort explanations. http://bugs.python.org/ file4451/timsort.txt. Version: 2002 [Rou12] Rougier, Nicolas P.: Matplotlib tutorial. http://www.loria.fr/ ~rougier/teaching/matplotlib/#d-plots. Version: 2012 [Sol12] Solem, Jan E.: O’Reilly, 2012 [Sum09] Summerfield, Mark: Programming in Python 3 (Second Edition) A Complete Introduction to the Python Language. 2. AddisonWesley Professional, 2009 Programming computer vision with python. 109