Valid XHTML     Valid CSS2    

Tuteur Python / Python Tutorial

gilles.hunault@univ-angers.fr

Attention : ce tuteur utilise Python 3.2 et non pas Python 2.7, c'est-à-dire une version 3 et non pas une version 2. Comme les deux versions ont de nombreuses incompatibilités, il faut être très prudent(e) si vous faites des copier/coller des instructions ou des programmes. De plus, comme Python utilise le symbole deux points et l'indentation pour définir et délimiter les blocs, nous utiliserons des fins explicites de bloc indiquées par des commentaires, ce qui nous parait être une bonne habitude à prendre, suivant en cela le fameux adage il faut favoriser la lisibilité du code plutôt que la vitesse d'écriture. On trouvera ici un pdf des différences entre python 2 et 3 (copie locale).

Comme mes autres tuteurs, ce texte est une introduction à un langage de programmation, pas à la programmation en général. On suppose donc ici que vous savez déjà programmer, mais dans un ou plusieurs autres langages. Un cours soutenu de Python 3 en français que nous conseillons est celui de B. Cordeau, avec ses exercices corrigés, disponible sur le site iut orsay (copies locales ici et ), mais il y en a d'autres, plus détaillés, notamment celui de G. Swinnen (copie locale) et enfin Python pour lycéens. En anglais, on pourra bien sûr consulter diveIntoPython3 et la documentation officielle Python v3.3.0 documentation.

Ce tuteur ne traite pas d'aspects avancés, comme la programmation objet, les décorateurs, la programmation fonctionnelle induite par map(), filter(), reduce() et ne détaille ni les générateurs ni les fonctions génératrices. C'est volontaire, afin de garder un format court. Les références citées (Cordeau et Swinnen) les traitent très bien (de plus en français !) et on s'y référera pour aller plus loin.

Quelques cartes de référence pour Python 3 : memento  abrege  py-qrc.

Table des matières

  1. Présentation de Python

  2. Syntaxe élémentaire

  3. Nos exemples standards

  4. Compléments pour les bases de données, le Web et XML

  5. Forces et faiblesses du langage

1. Présentation de Python

Python est un langage de scripts conçu pour être lisible et rapide. Développé après Perl, il se veut au moins aussi concis et efficace, notamment pour les listes. Apparu pendant «l'explosion du Web», il peut servir de langage de prototypage pour des applications Web et bases de données. Langage plutôt récent si on le compare à Perl, Awk ou Regina/Rexx, il bénéficie de plusieurs paradigmes de programmation : ainsi, il est fonctionnel et implémente proprement les concepts objets. Par contre, il n'est pas vectoriel (mais NumPy l'est).

Python a obtenu une certaine reconnaissance de la part de nombreuses communautés de programmeurs, parfois génés par les sigils de Perl et sans doute aussi grâce à l'indice TIOBE, notamment dans les années 2007 et 2010. De plus, le grand nombre de packages (sous-programmes) disponibles sur le site PyPI en fait un langage très complet : il y avait plus de 26 000 packages début décembre 2012 (54 400 en janvier 2015, 196 000 projets en 2019). Python s'interface très bien notamment avec TK, QT, GTK pour produire des interfaces graphiques. Python sert souvent -- et en particulier dans la communauté Linux -- à produire des scripts de configuration d'installation ou de gestion d'applications.

Enfin, Python est particulièrement apprécié pour le calcul scientifique via NumPy et plus généralement pour les sciences et techniques avec SciPy et aussi à cause de sa facilité à être interfacé avec C et Java. On oppose souvent Python, Perl et Ruby, sans doute par méconnaissance réciproque de ces langages, de leurs forces et faiblesses respectives.

Pour Linux, la commande sudo apt-get install python3 permet d'installer Python 3. Pour Windows, nous vous conseillons de passer par l'installeur de Active Python 3 sur le site Active State.

2. Syntaxe élémentaire

Python utilise l'indendation pour définir les blocs, ce qui le rend à la fois lisible et rapide à écrire, mais parfois illisible à l'impression. Il n'utilise pas de symbole spéciaux comme $ ou @ ou % comme en Perl et le typage y est implicite ou "dynamique", comme dans de nombreux langages de scripts. Par contre il n'est pas initialisant : toute variable doit être initialisée, contrairement à Perl, Awk ou Rexx. Lorsque Python est installé, il suffit juste de taper python ou python3 dans un shell, en ligne de commandes, pour tester Python en interactif. On en sort alors avec quit(). Taper ipython ou ipython3 fournit un environnement un peu plus technique et passer par un jupyer notebook est sans doute la version la plus élaborée d'un interpréteur interactif.

Sachant que = réalise une affectation et que print() permet d'afficher les résultats, on peut réaliser la session suivante :


     $gh> python3
     
     >>> a = 5
     
     >>> print(a)
     5
     
     >>> a += 3
     
     >>> print(a)
     8
     
     >>> quit()
     

Python distingue majuscules et minuscules dans le nommage des objets, variables et fonctions et il y a des recommandations sur la façon d'écrire le code nommées PEP 8 ou Style Guide for Python Code. Le symbole # sert pour les commentaires. Pour les entiers écrits dans une base autre que 10, on peut utiliser les préfixes 0b, 0o et 0x afin de désigner respectivement les entiers binaires, octaux et hexadécimaux. Pour réaliser des opérations mathématiques sur les réels autres que les opérations arithmétiques usuelles, il faut "importer" le module math, par exemple pour calculer un sinus.

L'affectation utilise = alors que la comparaison utilise ==. On peut affecter en cascade (nous ne le conseillons pas) et en parallèle, par exemple : x = y = 0 et x,y = 0,1. Ainsi a,b = b,a permute le contenu des variables a et b. Il n'existe pas de notation ++ comme en Perl. Par contre on peut utiliser le raccourci a += 1 pour réaliser l'incrémentation a = a + 1. On notera que Python définit trois constantes False, True et None. Pour les chaines de caractères, le mode triple quote permet de fournir des chaines écrites sur plusieurs lignes. + concaténe les chaines et la fonction len() (qui n'est pas une méthode) en fournit la longueur : par exemple len("pomme") renvoie 5. Les méthodes de chaines comme capitalize(), lower(), startswith() sont nombreuses. Voir le manuel de référence, section String Methods. L'utilisation d'index dans les chaines (y compris des valeurs négatives) et la spécification d'étendue avec le symbole deux-points permet d'extraire des sous-chaines. Voici quelques exemples vite faits, en interactif :


     python3
     
     >>> a = "pomme"
     
     >>> len(a)
     5
     
     >>> a.len()
     AttributeError: 'str' object has no attribute 'len'
     
     >>> a.capitalize()
     'Pomme'
     
     >>> "-" * len(a)
     '-----'
     
     >>> phr = "le chat mange la souris."
     
     >>> phr.split(" ")
     ['le', 'chat', 'mange', 'la', 'souris.']
     
     >>> phr[1]
     'e'
     
     >>> phr[-1]
     '.'
     
     >>> phr[3:]
     'chat mange la souris.'
     
     >>> phr.find("la")
     14
     
     >>> phr[ : phr.find("la") ]
     'le chat mange '
     
     >>> phr[ phr.find("la") : ]
     'la souris.'
     
     
     

Si print() permet d'écrire, la lecture au clavier se fait par input(), qui renvoie toujours du texte. Il faut donc transtyper explicitement lors de la lecture de valeurs numériques. Avec print(), on utilise la virgule comme séparateur et les séquences comme \n sont reconnues.

Pour les tests, la syntaxe if/else avec d'éventuels elif (pour else if) n'est pas classique comme en C ou Perl car il y a un délimiteur ouvrant de block nommé : (deux-points) mais pas de délimiteur fermant, vu que c'est l'indentation qui l'induit. En Python, tout ce qui est 0 ou vide est équivalent à faux, sans compter None. Nous conseillons fortement d'utiliser un commentaire pour visualiser la fin des blocs. Voici un exemple :


     # standard :
     
     age = int(input("donner votre age : "))
     if age < 18 :
        print("mineur")
     elif age==18 :
        print("juste majeur")
     else :
             print("majeur")
     
     print("bye")
     
     # conseillé :
     
     age = int(input("donner votre age : "))
     
     if age < 18 :
        print("mineur")
     elif age==18 :
        print("juste majeur")
     else :
        print("majeur")
     # fin de si sur age
     
     # 0 est comme faux,  "" et None aussi
     
     x = 0
     
     if x :
        print("vrai")
     else :
        print("faux") # c'est ce qui est affiché
     # fin de si
     
     print("bye")
     

Attention donc à l'indentation :


     $gh> cat if2.py
     
     age = int(input("donner votre age : "))
     if age < 18 :
     print("mineur") # incorrect car sans indentation !
     elif age==18 :
        print("juste majeur")
     else :
           print("majeur")
     print("bye")
     
     $gh> python3 if2.py
     
       File "if2.py", line 3
         print("mineur")
             ^
     IndentationError: expected an indented block
     
     
     

Remarque : il est également possible d'utiliser la constante None pour définir endif, endfor etc. On peut alors facilement reformater les programmes-sources Python, développer des macros pour éditeur... 


     # une autre syntaxe possible en Python via None
     
     endif = None
     
     age = int(input("donner votre age : "))
     
     if age < 18 :
        print("mineur")
     elif age==18 :
        print("juste majeur")
     else :
        print("majeur")
     endif
     
     print("bye")
     

On notera que Python dispose d'une affectation conditionnelle binaire plus concise, sous la forme x = a if ... else b .

Les structures de données standards de Python sont les séquences (chaines, listes, tuples ou étendues), les tableaux associatifs ou dictionnaires et les ensembles. On peut sans doute y adjoindre les fichiers-textes. Dans les listes et les tuples, les éléments sont séparés par des virgules. On entoure les éléments d'une liste définie en extension par des crochets alors qu'on utilise des parenthèses pour les tuples. La liste vide est [ ] et le tuple vide ( ). Une liste définie en intension se définit par une expression sur une séquence via for/in. Les méthodes de listes comme sort(), append(), reverse() sont nombreuses. Voir le manuel de référence, section list tuple range. La fonction range() permet de générer des entiers en progression arithmétique. A l'aide d'indices et de tranches via le symbole : on peut extraire des élements et des sous-listes alors que pour sélectionner des élements et des sous-listes on utilise for/in/if/where... Un tuple est comme une liste, mais ses éléments sont non modifiables. Les listes et les tuples sont l'équivalent des tableaux des langages classiques, mais sans imposer d'avoir le même type pour tous les éléments. Une liste peut contenir des tuples et réciproquement. Le terme précis anglais immutable (soit immuable, en français) s'applique aux chaines et aux tuples.


     python3
     
     >>> a = range(10)
     
     >>> print(a) # pas de chance !
     range(0, 10)
     
     >>> print([x for x in a ])
     [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
     
     >>> les nombres pairs par multiplication
     >>> [ x*2 for x in range(11) ]
     [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
     
     >>> les carrés des nombres pairs
     >>> y = [ x**2 for x in range(11) if x%2== 0 ]
     
     >>> les trois derniers
     >>> y[  -3 : ]
     [36, 64, 100]
     
     >>> y.pop()
     100
     
     >>> y.reverse() # mais on ne voit rien !
     
     >>> y
     [64, 36, 16, 4, 0]
     
     >>> tuple = 1,2,"oui",4 # tuple par défaut
     
     >>> tuple
     (1,2,'oui',4)
     
     >>> tuple   = (1,2,"oui",4) # tuple explicite
     
     >>> liste = ["oui",25,"non"]
     
     >>> tl = (liste,"12")
     >>> tl
     (['oui', 25, 'non'], '12')
     
     >>> ltl  = [5,tl,"pomme"]
     >>> ltl
     [5, (['oui', 25, 'non'], '12'), 'pomme']
     
     >>> # python n'est pas vectoriel
     
     >>> v1 = [1,2,3]
     
     >>> v2 = [4,5,6]
     
     >>> v1
     [1, 2, 3]
     
     >>> v1+1
     Traceback (most recent call last):
       File "<stdin>", line 1, in <module>
     TypeError: can only concatenate list (not "int") to list
     
     >>> v1+v2
     [1, 2, 3, 4, 5, 6]
     
     

L'utilisation en cascade peut aboutir à des expressions efficaces mais pas toujours lisibles comme


     $gh> python3
     
     >>> import string
     >>> exclude = set(string.punctuation)
     >>> lig =  " un drole d'exemple ? Oui ! mais avec, sinon ; un . "
     
     >>> print(lig)
      un drole d'exemple ? Oui ! mais avec, sinon ; un .
     
     >>> lig = ''.join(ch for ch in lig if ch not in exclude)
     >>> print(lig)
      un drole dexemple  Oui  mais avec sinon  un
     
     

Les dictionnaires utilisent des accolades et le symbole deux-points pour la définition en extension. Sinon, on peut utiliser la notation en tableau avec des indices caractères ou numériques. Les ensembles, définis par les accolades utilisent les opérations ensemblistes -, | , & et la fonction d'appartenance notée in. Il faut bien sûr du temps et de l'entrainement pour profiter de ces structures. Il y a de nombreuses fonctions et méthodes pour les dictionnaires dont len( ), del( ), key( ), items( ), values( )... Voir le manuel de référence, section dict.

En plus de la boucle while, Python dispose d'une boucle for qui est étendue à tous les objets dits itérables. Dans une boucle, on peut utiliser break, continue et, de façon plus surprenante mais "agréable", else. La gestion des "exceptions" comme on dit, se fait classiquement avec try, except (au lieu du catch usuel), else, finally et raise, bien sûr.

Les expressions régulières sont accessibles via le module re. Les fonctions et méthodes sont nommées re.search( ), re.sub( ), re.compile( ). Voir le manuel de référence, section regular expressions.

La lecture des fichiers texte est classique avec des fonctions comme open( ) et print( ), des méthodes comme read( ), readline( ), close( )... La fonction print sert pour la console (sortie standard) et les fichiers. L'argument end= permet de définir ce qui est utilisé en fin de ligne, soit le caractère \n par défaut. On peut aussi choisir le séparateur via sep=.

Pour définir une fonction, on utilise le mot-clef def et return sert à indiquer ce qu'on renvoie. Si on regroupe des fonctions dans un fichier, cela s'appele un module. Regrouper des modules donne des packages. Parmi les modules et packages standards, on peut citer os, datetime, re, string, codecs... On peut tout charger via import (mais sans mettre le type .py du fichier). Il est possible de n'importer que certaines fonctions avec from/import et d'"aliaser" avec as. Pour recharger en session interactive un fichier Python, il faut utiliser reload(). Les paramètres d'une fonction sont nommables et peuvent avoir une valeur par défaut, mais on peut aussi utiliser le positionnement pour passer les paramètres. On peut passer des tuples et des dictionnaires mais en dernier argument, seulement, avec la notation * et **. Voici un exemple de module, le fichier python_demo5.py qui implémente deux fonctions :


     # (gH)   -_-  python_demo5.py
     
     def plusV1(x,y)  :
         ''' realise l'addition de deux nombres '''
         return( x+y )
     # fin de fonction plusV1
     
     def plusV2(x,y=1)  :
         ''' realise l'addition de deux nombres '''
         ''' ou renvoie x+1 x si y n'est pas fourni '''
         return( x+y )
     # fin de fonction plusV
     
     
     

Et voici des exemples d'utilisation :


     >>> import python_demo5
     
     >>> plusV1(3,5)
     Traceback (most recent call last):
       File "<stdin>", line 1, in <module>
     NameError: name 'plusV1' is not defined
     
     >>> python_demo5.plusV1(3,5)
     8
     
     >>> a = python_demo5.plusV2(3)
     >>> a
     4
     
     >>> from python_demo5 import plusV1
     >>> plusV1(6,3)
     9
     
     >>> plusV1(x=6,y=3)
     9
     
     >>> plusV1(y=6,x=2)
     8
     
     >>> plusV1(x=6)
     Traceback (most recent call last):
       File "<stdin>", line 1, in <module>
     TypeError: plusV1() takes exactly 2 arguments (1 given)
     
     >>> a = python_demo5.plusV2(x=3)
     
     >>> a = python_demo5.plusV2(y=3)
     Traceback (most recent call last):
       File "<stdin>", line 1, in <module>
     TypeError: plusV2() takes at least 1 argument (1 given)
     
     

Une particularité de Python qui fait partie de la philosophie Python est de fournir des tests unitaires. En principe, un module appelé comme programme principal doit effectuer des tests. En tant que sous-programme, il doit juste fournir des fonctions. Voici un exemple de ce qu'il est courant de faire en "auto-test" qui doit être placé après les définitions de fonctions :


     # (gH)   -_-  python_demo6.py
     
     def plus(x,y)  :
         ''' la fonction plus realise l'addition de deux nombres '''
         return( x+y )
     # fin de fonction plus
     
     # auto-test
     
     if __name__ == "__main__" :
         help(plus)
         print("Voici ce que produit plus(x=3,y=4)")
         print( plus(3,4) )
     # fin de l'auto-test
     

Voici ce qui se passe quand on utilise ce fichier :

  • si on exécute directement le fichier, l'auto-test est déclenché. On voit alors l'aide puis l'exécution du code de test.

  • si on importe le fichier, l'auto-test n'est pas déclenché. On ne voit rien de spécial mais les fonctions sont disponibles.

3. Nos exemples standards

Voici notre version de bonjour avec conversion en majuscule, affichage de la date et de l'heure.


     # -*- coding:latin1 -*-
     
     import time # module de gestion des fonctions liées au temps
     
     print("Bonjour.")
     prenom = input("Quel est ton prenom ? ")
     if (prenom==""):
        prenom = "inconnu(e)"
     else:
        prenom = prenom.upper()
     # endif
     print("Merci. Le "+time.strftime('%d/%m/%y',time.localtime()),end="")
     print(" à "+time.strftime('%H:%M',time.localtime())+" au revoir, "+prenom+".")
     
     
     

On notera la différence de syntaxe avec le même exemple en Python 2


     import time # module de gestion des fonctions liées au temps
     
     print "Bonjour."
     prenom = raw_input("Quel est ton prenom ? ")
     if (prenom==""):
        prenom = "inconnu(e)"
     else:
        prenom = prenom.upper()
     # endif
     # la virgule en fin fait qu'il n'y a pas de retour-charriot
     print "Merci. Le "+time.strftime('%d/%m/%y',time.localtime()) ,
     print " a "+time.strftime('%H:%M',time.localtime())+" au revoir,"+prenom.upper()
     
     

mais avec le même résultat


     Bonjour.
     Quel est ton prenom ? gilles
     Merci. Le 06/12/12 à 12:01 au revoir, GILLES.
     

Comme deuxième exemple classique, nous utiliserons tout d'abord le passage de paramètre en ligne de commande, avec affichage de l'aide si aucun paramètre et bouclage si le paramètre n'est pas un entier. On notera que le formatage des sorties passe par l'opérateur %. mais on peut aussi utiliser la fonction format.


     # (gH)   -_-  tdmv1.py  ;  TimeStamp (unix) : 06 Décembre 2012 vers 13:40
     
     import sys
     import re
     
     # aide éventuelle
     
     if (len(sys.argv)==1) :
             aide = '''
     
             tdmv1.py ; table de multiplication d'un entier entre 1 et 10
     
             syntaxe : python3 tdmv1.py NOMBRE
             exemple : python3 tdmv1.py 7
             '''
             print(aide)
             sys.exit()
     # fin de si
     
     # on veut un entier, soit l'expression régulière "^[0-9]+$"
     
     nblu = sys.argv[1]
     while not re.search("^[0-9]+$",nblu) :
       print("Désolé, "+nblu+" n'est pas un entier.")
       print("Donnez un entier entre 1 et 10 : ",end="")
       nblu = input()
     # fin tant que
     
     print("\nMerci.\n")
     nb = int(nblu)
     
     # arrivé ici nb est bien un entier positif entre 1 et 10
     
     print("Table de multiplication de ",nb)
     
     for ifois in range(1,11) :
          prod = ifois * nb
          ligr = ('%2d' % ifois) + " fois " + str(nb) + " = " + ("%3d" % prod)
          print( ligr )
     # fin pour ifois
     

Une version "plus moderne" viendrait utiliser par exemple easygui.py pour effectuer une saisie dans un panneau comme sur l'image ci-dessous :

                         saisie-python.png


     # (gH)   -_-  tdmv2.py  ;  TimeStamp (unix) : 06 Décembre 2012 vers 14:20
     
     import sys
     import re
     from   easygui import *
     
     # on demande un entier avec integerbox de eaysygui
     
     nblu = integerbox("Donnez un entier entre 1 et 10 ","Pour tdmv2.py",7,1,10)
     
     # on veut un entier, soit l'expression régulière "^[0-9]+$"
     # mais l'utilisateur peut annuler la saisie (cancel)
     
     if not type(nblu) is int : # en fait, ce doit être None...
       print("Dommage, revenez quand vous voulez.")
       sys.exit()
     # fin si
     
     print("\nMerci.\n")
     nb = int(nblu)
     
     # arrivé ici nb est bien un entier positif entre 1 et 10
     
     print("Table de multiplication de ",nb)
     
     for ifois in range(1,11) :
          prod = ifois * nb
          ligr = ('%2d' % ifois) + " fois " + str(nb) + " = " + ("%3d" % prod)
          print( ligr )
     # fin pour ifois
     

Si vous avez bien suivi ce qui a été dit ci-dessus sur l'auto-test, vous ne serez pas étonné(e) de voir une démonstration de easygui juste en tapant python3 easygui.py soit l'affichage suivant :

                         easygui_autotest.png

Easygui n'est pas la seule possibilité d'interface graphique, loin de là. Python 3 peut s'interfacer avec Tk, soit TkInter et avec Qt, soit PyQt ou encore PyGtk (voir aussi pygtk3), wxpython...

Nous finirons les exemples standards avec la lecture du fichier examen.notes pour lequel on veut un affichage des élèves par ordre alphabétique et par ordre de mérite :


     BOBY        *         18 5  5
     
        ZELYNOU     *         6 11 7
        ANTIN       *     8 4  10
        BOB         *    16 8 15
        IZEL        *     16 18 12
     

Nous laissons au lecteur et à la lectrice le soin de décortiquer le script-solution :


     # (gH)   -_-  moynotes.py  ;  TimeStamp (unix) : 06 Décembre 2012 vers 14:42
     
     import sys
     import os
     import re
     
     nomfic = "examen.notes"
     if (not os.path.isfile(nomfic)) :
          print("le fichier "+nomfic+" n'existe pas. Stop.\n")
          sys.exit()
     # fin de si
     
     # lecture du fchier
     
     classe = {}
     fh     = open(nomfic,"r")
     nbe    = 0 # nombre d'élèves
     for lig in fh :
         lig = lig.strip()
         if len(lig)==0 :
            continue
         else :
            nbe += 1
            lig = re.sub("\s+"," ",lig)
            dec = lig.split("*")
            tdn = dec[1].split(" ")
            classe[dec[0]] = sum([int(note) for note in tdn if len(note)>0])/len(tdn)
         # fin si
     # fin de pour lig
     fh.close()
     print("Il y a ",nbe," élèves.")
     
     ##################################################################################
     
     def afficheNomMoy(nom,moy) :
     
         ''' un affiche formaté du nom et de la moyenne '''
     
     ##################################################################################
     
         if not type(moy) is float :
            moy = float(moy)
         # fin si
     
         print("   %-15s %6.2f" % (nom,moy) )
     
     # fin de fonction afficheNomMoy
     
     ##################################################################################
     
     # affichage alphabétique
     
     print("Voici les notes")
     merite = {}
     for (k,v) in sorted(classe.items()) :
       afficheNomMoy(nom=k,moy=v)
       fmt = ("%06.2f" % v) + "_" + k
       merite[fmt] = fmt
     # fin pour (k,v)
     
     # tri par ordre de mérite
     
     print("Tri décroissant des notes")
     for f in sorted(merite.values(),reverse=True) :
       (v,k) = f.split("_")
       afficheNomMoy(k,v)
     # fin pour v
     
     

4. Compléments pour les bases de données, le Web et XML

Interfacer une base de données par exemple en Mysql se fait très simplement en Python 3 avec MySQLdb ou avec PyMysql. Aujourd'hui (2012) Python sait bien sûr gérer Sqlite :


     $gh>python3
     
     >>> import pymysql  # https://github.com/petehunt/PyMySQL/
     
     >>> cnx = pymysql.connect(host="localhost",user="anonymous",passwd="anonymous",db="statdata")
     >>> cur = cnx.cursor()
     >>> qry = "show tables"
     >>> cur.execute(qry)
     27
     
     >>> for res in cur.fetchall() :
     ...    print(res)
     ... # fin pour
     
     ('art',)
     ('biopsies',)
     ('centres',)
     ('cli',)
     ('cmd',)
     ('coursFC',)
     ('elf',)
     ('elfetud',)
     ('four',)
     ('fragments',)
     ('images',)
     ('lames',)
     ('leadata',)
     ('medecins',)
     ('metavirs',)
     ('partiFC',)
     ('patients',)
     ('protocoles',)
     ('ronfle',)
     ('sen_CALC',)
     ('sen_GEOG',)
     ('sen_HIST',)
     ('stagesMaitrise',)
     ('titanic',)
     ('tmpElf',)
     ('typeslames',)
     ('vins',)
     
     >>> qry= "SELECT iden, age FROM elf WHERE sexe=1 ORDER BY iden LIMIT 3"
     >>> cur.execute(qry)
     3
     
     >>> for res in cur.fetchall() :
     ...     print(res)
     ... # fin pour
     ('M001', 62)
     ('M003', 31)
     ('M004', 27)
     
     >>> cur.close()
     >>> cnx.close()
     >>> quit()
     
     

Puisque Python a grandi avec le Web, il sait accéder aux fichiers du Web. Ainsi, voici comment récupérer le texte de notre page Web ndjm.php dont l'URL est http://forge.info.univ-angers.fr/~gh/internet/ndjpm.php et qui fournit le nombre de jours par mois si on lui passe en mode GET une valeur "raisonnable" du paramètre m :


     #  (gH)   -_-  getndjpm.py  ;  TimeStamp (unix) : 06 Décembre 2012 vers 15:52
     
     import http.client
     
     url = "forge.info.univ-angers.fr"
     prm = "/~gh/internet/ndjpm.php?m=12"
     
     cnx = http.client.HTTPConnection(url)
     cnx.request("GET",prm)
     repGh     = cnx.getresponse()
     contenu   = repGh.read()
     
     print(contenu)
     
     cnx.close()
     

Vous pouvez vérifier l'adéquation avec la page Web en cliquant ici pour m=12.

Rien de plus facile que de "monter" dynamiquement un serveur Web avec Python 3 : il y a un module (standard) nommé http.server ! Ainsi, avant d'exécuter le programme suivant, vérifiez que l'adresse http://localhost:8090 est bien inutilisée (votre navigateur doit répondre Unable to connect). Lancez ce programme


     # # (gH)   -_-  serveurweb.py  ;  TimeStamp (unix) : 26 Janvier 2015 vers 12:08
     
     ##  un mini-serveur Web en python3
     ##  adresse du serveur web : http://localhost:8090
     
     import http.server
     prt = 8090
     srv = http.server.HTTPServer(('', prt), http.server.CGIHTTPRequestHandler)
     srv.serve_forever()
     

Rien ne se passe à l'écran (sauf le prompt qui n'est pas revenu). Si maintenant vous utilisez dans un navigateur l'adresse http://localhost:8090 alors, dans le terminal, vous voyez le détail du protocole http... et sans doute la liste des fichiers de votre répertoire. Vous pouvez arrêter le programme avec Controle-C.

La notion de test (unitaire, de charge, d'exécution...) est fondamentale en Python. C'est pourquoi tester l'exécution de page Web via Sélénium est un jeu d'enfants en Python comme avec le montre le code suivant :


     # -*- coding:latin1 -*-
     ## fichier inscriptionIncorrectePOSTDejaInscrit.py
     
     # on essaie de créer un administrateur de nom admin et de mot de passe admin
     # mais il existe déjà, la page renvoie "already taken"
     
     from selenium import webdriver
     from selenium.webdriver.common.keys import Keys
     import time
     import re
     
     browser = webdriver.Firefox()
     
     try :
        page = "http://localhost/~gh/internet/Lte/login07.php"
        browser.get(page) ;
        browser.find_element_by_id("usernameSI").send_keys("admin")
        browser.find_element_by_id("passwordSI").send_keys("admin")
        browser.find_element_by_id("confirm_passwordSI").send_keys("admin"+Keys.RETURN)
        time.sleep(3)
        jsCode  = ' errZoneSI = window.document.getElementById("errorZoneSI") ; '
        jsCode += ' res = /already taken/.test(errZoneSI.value) ; '
        jsCode += ' return( res ) ; '
        res = browser.execute_script(jsCode)
        if (res) :
           print(" OK test inscriptionIncorrectePOSTDejaInscrit SUCCESS ")
        else :
           print(" NO test inscriptionIncorrectePOSTDejaInscrit FAILED (2) ")
        # fin de si
     
     except :
        print( " NO test inscriptionIncorrectePOSTDejaInscrit FAILED (1) ")
     
     browser.quit()
     
     

La lecture de fichiers XML ou l'exécution de transformation XSL en Python 3 ne pose là encore aucun problème, que ce soit avec les packages nommés xml, BeautifulSoup, ce qui inclut lxml.etree, xml.dom, xml.dom.minidom, xml.parsers, xml.etree.ElementTree... (mais comment choisir ?). Voici un exemple avec le fichier test3.xml et la transformation vide.xsl :


     >>> import xml.etree.cElementTree as ET
     
     >>> arb = ET.ElementTree(file='test3.xml')
     
     >>> arb
     <ElementTree object at 0x2217e50>
     
     >>> type(arb)
     <class 'ElementTree'>
     
     >>> for elt in arb.getroot() :
     ...    print(elt)
     ... # fin pour
     <Element 'personne' at 0x21ffcf0>
     <Element 'personne' at 0x21ffd80>
     <Element 'personne' at 0x21ffe40>
     
     >>> for elt in arb.getroot() :
     ...    for x in elt :
     ...        print(x)
     ...
     <Element 'nom' at 0x21fff30>
     <Element 'service' at 0x21fff60>
     <Element 'nom' at 0x21fffc0>
     <Element 'service' at 0x2228030>
     <Element 'nom' at 0x2228090>
     <Element 'service' at 0x22280c0>
     
     >>> for elem in arb.findall("//nom") :
     ...    print(elem)
     ...
     <Element 'nom' at 0x21fff30>
     <Element 'nom' at 0x21fffc0>
     <Element 'nom' at 0x2228090>
     
     >>> from lxml import  etree
     >>> xml = etree.parse("test3.xml")
     >>> xsl = etree.parse("vide.xsl")
     >>> tra = etree.XSLT(xsl)
     >>> tra(xml)
     <lxml.etree._XSLTResultTree object at 0x7fbab6dff520>
     
     >>> res = tra(xml)
     
     >>> print(res)
     <?xml version="1.0" encoding="ISO-8859-1"?>
     
     
         Dupond
         Achats
     
     
         Durand
         Achats
     
     
         Dupuis
         Courrier
     
     >>> quit()
     

5. Forces et faiblesses du langage

Pour quelqu'un qui ne connait aucun langage de script, Python peut semble concis, rapide, voire "élégant". Pour un perl-mongueur, il y a des "choses intéressantes", comme ils disent. En particulier une lisibilité accrue par l'indentation et l'absence de sigils. De plus, le grand nombre de fonctions et de méthodes permet d'accéder rapidement à une solution classique d'un problème classique. Par contre, pour un habitué des langages objets, il semble y avoir quelques incohérences, justement à cause des fonctions et des méthodes.

Par exemple si ch est la chaine "le chat et la souris" il faut utiliser la fonction len() sous la forme len(ch) pour connaitre la longueur de la chaine alors que pour trouver la position de "a" il faut utiliser la méthode find sous la forme ch.find("a"). Pourquoi ne pas avoir défini -- aussi -- ch.length() ? Après réfexion, d'autres fonctions aussi pourraient être plus objet, comme print(), open()...

Dans un autre registre, si Python est fonctionnel, il n'est pas vectoriel. En d'autres termes il ne sait pas manipuler directement des structures terme à terme. Par exemple, tout le monde apprend à l'école à additionner les composantes (x,y) des points dans le plan. Certains langages dont R sont capables de calculer c(2,3) + c(4,5) pour trouver (6,8) sans passer par des constructions particulières.

Au final, Python 3 est un bon langage de scripts, plus lisible et mieux conçu que Perl 5 mais sans lui être très nettement supérieur. De plus, il reste nettement «perfectible». Ainsi, le nombre de packages possibles pour une même tâche pose l'embarras du choix et de l'évaluation de ces packages, sans parler de la documentation, parfois inexistante. Python (surtout version 2) est cependant très utilisé, comme Perl et Ruby, à cause d'une forte communauté active sur le Web. Ainsi la PIL (Python Imaging Library) est un package reconnu en traitement d'images.

On pourra lire avec profit la brochure python, le developpement autrement du site alterway.fr (copie locale) qui résume bien tout ce qu'on peut attendre de Python comme langage de développement.

Si maintenant on s'intéresse à Python comme langage scientifique, c'est un outil très efficace, presque incontournable, beaucoup plus généraliste que R. On pourra s'en convaincre en lisant les Scipy Lecture Notes. En particulier les np.array utilisent des fonctions vectorielles, comme le montre l'exemple suivant :


     $gh> ipython
     
     Python 3.5.5 |Anaconda, Inc.| (default, May 13 2018, 21:12:35)
     Type 'copyright', 'credits' or 'license' for more information
     IPython 6.4.0 -- An enhanced Interactive Python. Type '?' for help.
     
     In [1]: import numpy as np
     
     In [2]: v1 = np.array([2,3])
     
     In [3]: v2 = np.array([4,5])
     
     In [4]: v1 + v2
     Out[4]: array([6, 8])
     
     In [5]: sum(v1,v2)
     Out[5]: array([ 9, 10])
     
     In [6]: sum(v1)
     Out[6]: 5
     
     In [7]: v1.sum()
     Out[7]: 5
     
     

Il est donc très important de consulter les documentations de NumPy, Pandas, Seaborn et autres Scikit-learn avant de parler du seul langage Python pour se faire une idée de ce que Python représente...

 

 

retour gH    Retour à la page principale de   (gH)