Plongée dans Python


précédentsommairesuivant

II. Chapitre 1 : Votre premier programme Python

II-A. Allons y !

Les conventions usuelles voudraient que je démarre ce tutoriel en vous ennuyant avec les éléments fondamentaux de la programmation afin que nous puissions travailler doucement pour construire quelque chose d’utile. Passons outre. Voici un exemple de programme Python complet et qui fonctionne. Il n’a probablement aucun sens pour vous. Ne vous inquiétez pas : on va le disséquer ligne par ligne. Mais commencez par le lire et voyez ce que vous pouvez en comprendre.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
SUFFIXES = {1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
            1024: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']}

def approximate_size(size, a_kilobyte_is_1024_bytes=True):
    '''Convert a file size to human-readable form.

    Keyword arguments:
    size -- file size in bytes
    a_kilobyte_is_1024_bytes -- if True (default), use multiples of 1024
                                if False, use multiples of 1000

    Returns: string

    '''
    if size < 0:
        raise ValueError('number must be non-negative')

    multiple = 1024 if a_kilobyte_is_1024_bytes else 1000
    for suffix in SUFFIXES[multiple]:
        size /= multiple
        if size < multiple:
            return '{0:.1f} {1}'.format(size, suffix)

    raise ValueError('number too large')

if __name__ == '__main__':
    print(approximate_size(1000000000000, False))
    print(approximate_size(1000000000000))

Le moment est venu d’exécuter ce programme depuis la ligne de commande. Sur Windows, vous devriez avoir quelque chose qui ressemble à ceci :

 
Sélectionnez
c:\home\diveintopython3\examples> c:\python31\python.exe humansize.py
1.0 TB
931.3 GiB

Sur Max OSX ou Linux, ça ressemblera plutôt à ceci :

 
Sélectionnez
you@localhost:~/diveintopython3/examples$ python3 humansize.py
1.0 TB
931.3 GiB

Que vient-il de se passer ? Vous avez exécuté votre premier programme Python. Vous avez appelé l’interpréteur Python depuis la ligne de commande et lui avez donné le nom du script que vous vouliez exécuter. Ce script définit une seule fonction, la fonction approximate_size(), qui à partir d’une taille exacte de fichier en octets (bytes) renvoie cette même taille sous une forme plus jolie (mais approximative). Vous avez déjà probablement vu cela avec Windows Explorer, ou avec Mac OS X Finder, ou encore avec Nautilus, Dolphin ou Thunar sous Linux. Si vous faites afficher un dossier de documents sous la forme de listes à plusieurs colonnes, alors apparaît une table avec l’icône du document, la taille, le type, la date de dernière modification, etc. Si le dossier contient un fichier de 1093 octets, votre gestionnaire de fichiers n’affichera pas 1093 octets, mais plutôt quelque chose comme 1Ko à la place. C’est ce que calcule la fonction approximate_size().

Regardez à la fin du script les deux appels à print(approximate_size(arguments)). Ce sont des appels de fonction. approximate_size() est d’abord appelée en lui passant un certain nombre d’arguments, puis la valeur de retour de cette fonction est elle-même directement passée en argument de la fonction print(). La fonction print() est une fonction intégrée à Python. Vous n’en verrez pas de déclaration explicite. Vous pouvez simplement l’utiliser n’importe quand, n’importe où. Il y a beaucoup d’autres fonctions intégrées (et beaucoup plus encore dans des modules séparés), mais patience…

Pourquoi lancer le script en ligne de commande vous renvoie toujours le même résultat ? On va voir cela. Mais d’abord, regardons de plus près la fonction approximate_size().

II-B. Déclarer une fonction

Python a des fonctions comme la plupart des autres langages, mais on n’a pas besoin d’avoir de fichier de prototypes (ou fichiers de déclarations, communément appelés entêtes ou headers) comme en C++ ou avec un sectionnement interface/implémentation comme en Pascal. Quand vous avez besoin d’une fonction, déclarez la simplement comme ceci :

 
Sélectionnez
def approximate_size(size, a_kilobyte_is_1024_bytes=True):

La déclaration de la fonction débute par le mot-clef def suivi du nom de la fonction et suivi d’arguments entre parenthèses. Les arguments sont séparés par des virgules.

Notez que la fonction ne définit pas le type de la valeur qu’elle renvoie. Les fonctions Python ne sont pas typées ; elles ne spécifient d’ailleurs même pas si elles retournent une valeur ou non. (En fait, toutes les fonctions Python retournent une valeur : si la fonction exécute une instruction return, la fonction renvoie cette valeur ; sinon, la fonction va retourner None qui est la valeur nulle ou vide de Python).

Dans certains langages, les fonctions (qui retournent une valeur) démarrent par function et les subroutines ou procédures (qui ne retournent pas de valeur) commencent par sub. Il n’y a pas de subroutines en Python. Tout est fonction et toutes les fonctions renvoient une valeur (même si elle vaut éventuellement None). Elles débutent toutes par le mot-clef def.

La fonction approximate_size() prend deux arguments – size et a_kilobyte_is_1024_bytes – mais aucun type n’est spécifié pour ceux-ci. En Python, les variables ne sont jamais explicitement typées. Python devine de quel type est une variable, et en garde la trace en interne.

En Java ou dans d’autres langages typés statiquement, vous devez spécifier le type des arguments d’une fonction ainsi que celui de sa valeur de retour. En Python, vous n’avez jamais à spécifier explicitement le type de quoi que ce soit. En se basant sur la valeur que vous assignez à une variable, Python garde une trace du type de la donnée en interne.

II-B-1. Arguments nommés et optionnels

Python permet de construire des fonctions dont les arguments peuvent avoir des valeurs par défaut. Ainsi, si la fonction est appelée sans argument, les arguments prennent les valeurs par défaut. De plus les arguments peuvent être spécifiés dans n’importe quel ordre en utilisant les arguments nommés.

Regardons à nouveau la déclaration de la fonction approximate_size() :

 
Sélectionnez
def approximate_size(size, a_kilobyte_is_1024_bytes=True):

Le deuxième argument, a_kilobyte_is_1024_bytes, spécifie True comme valeur par défaut. Ceci signifie que l’argument est optionnel : vous pouvez appeler la fonction sans et Python agira comme si vous aviez mentionné True comme second paramètre.

Considérons à présent la fin du script

 
Sélectionnez
if __name__ == '__main__':
    print(approximate_size(1000000000000, False))   ### (1)
    print(approximate_size(1000000000000))          ### (2)

(1) Ceci appelle la fonction approximate_size() avec deux arguments. À l’intérieur de la fonction, a_kilobyte_is_1024_bytes vaut False étant donné que vous l’avez stipulé explicitement comme second argument.

(2) Ceci appelle la fonction approximate_size() avec un seul argument,ce qui ne pose pas de problème puisque le second argument est optionnel ! Étant donné que vous ne l’avez pas spécifié, il prend la valeur par défaut True définie dans la déclaration de la fonction.

Vous pouvez aussi passer des valeurs à une fonction en les nommant :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
>>> from humansize import approximate_size
>>> approximate_size(4000, a_kilobyte_is_1024_bytes=False)       ### (1)
'4.0 KB'
>>> approximate_size(size=4000, a_kilobyte_is_1024_bytes=False)  ### (2)
'4.0 KB'
>>> approximate_size(a_kilobyte_is_1024_bytes=False, size=4000)  ### (3)
'4.0 KB'
>>> approximate_size(a_kilobyte_is_1024_bytes=False, 4000)       ### (4)
  File "<stdin>", line 1
SyntaxError: non-keyword arg after keyword arg
>>> approximate_size(size=4000, False)                           ### (5)
  File "<stdin>", line 1
SyntaxError: non-keyword arg after keyword arg

(1) Ceci appelle la fonction approximate_size() avec la valeur 4000 pour le premier argument (size) et False pour l’argument nommé a_kilobyte_is_1024_bytes (ici cela correspond au second argument, mais peu importe comme on va le voir dans un instant).

(2) Ceci appelle la fonction approximate_size() avec la valeur 4000 pour l’argument nommé size et False pour l’argument nommé a_kilobyte_is_1024_bytes (ici ces arguments nommés sont spécifiés dans le même ordre que dans la déclaration de la fonction, mais peu importe).

(3) Ceci appelle la fonction approximate_size() avec la valeur False pour l’argument nommé a_kilobyte_is_1024_bytes et la valeur 4000 pour l’argument nommé size (Voyez-vous ? Je vous avais dit que l’ordre n’importait pas lorsqu’on nomme les arguments !).

(4) Ceci provoque une erreur car vous ne pouvez pas appeler un argument nommé avant un argument non nommé (ou argument positionnel). On commence toujours par spécifier les arguments non nommés (là, l’ordre est important) puis les arguments nommés (ceux-là, dans l’ordre que l’on veut)

(5) Ceci provoque une erreur également pour la même raison que précédemment. Surprenant ? Après tout, vous passez 4000 à l’argument nommé size, donc « évidemment » que False est la valeur pour l’argument a_kilobyte_is_1024_bytes. Mais Python ne fonctionne pas comme cela. Dès que vous avez un argument nommé, tous les arguments suivants doivent eux aussi être nommés.

II-C. Écrire du code lisible : la  docstring

Je ne veux pas vous ennuyer avec un long sermon moralisateur sur l’importance de la documentation du code. Sachez juste qu’un script est écrit une seule fois, mais lu de nombreuses fois et l’audience la plus importante pour votre code est vous-même, 6 mois après l’avoir écrit (c’est-à-dire après avoir tout oublié de lui, mais quand vous avez besoin de vous remettre dedans pour corriger un bogue). Avec Python, c’est facile d’écrire du code lisible, donc tirez-en parti. Vous me remercierez dans 6 mois.

Vous pouvez documenter une fonction Python en lui attribuant une docstring (raccourci de l’anglais documentation string, le mot string signifiant chaîne de caractères). Dans ce programme, la fonction approximate_size() a une docstring :

 
Sélectionnez
def approximate_size(size, a_kilobyte_is_1024_bytes=True):
    '''Convert a file size to human-readable form.

    Keyword arguments:
    size -- file size in bytes
    a_kilobyte_is_1024_bytes -- if True (default), use multiples of 1024
                                if False, use multiples of 1000

    Returns: string

    '''

Toute fonction mérite une bonne docstring.

Les triples apostrophes indiquent une chaîne de caractères sur plusieurs lignes. Tout ce qui se trouve entre le début et la fin des apostrophes triples constitue une seule et unique chaîne de caractères, y compris les retours à la ligne, les espaces et aussi les éventuels guillemets et apostrophes. Vous pouvez l’utiliser n’importe où, mais vous verrez cela le plus souvent pour définir une docstring.

Les triples apostrophes sont aussi un moyen facile de définir une chaîne de caractères possédant à la fois des apostrophes simples et doubles, comme on le ferait avec qq/…/ en Perl 5.

Tout ce qui est entre les triples apostrophes constitue la docstring de la fonction qui renseigne sur ce que la fonction fait. Une docstring, si elle existe, doit être la première chose définie dans une fonction (c’est-à-dire, sur la ligne qui suit la déclaration de la fonction). Techniquement, vous n’avez pas besoin de fournir une docstring à votre fonction, mais vous devriez toujours le faire. Je sais que vous avez entendu cela dans tous les cours de programmation que vous avez suivis, mais Python vous donne une motivation supplémentaire : la docstring est disponible lors de l’exécution comme un attribut de la fonction.

Beaucoup d'environnements de développement (IDE) Python utilisent les  docstrings pour fournir une documentation contextuelle. Ainsi lorsque vous tapez le nom d’une fonction, sa docstring apparaît en info-bulle.

II-D. La recherche du chemin d’import

Avant d’aller plus loin, faisons brièvement mention de la recherche des bibliothèques. Python regarde à plusieurs endroits lorsque vous essayez d’importer un module. En particulier, il regarde dans tous les répertoires définis dans sys.path. Ce n’est qu’une liste et vous pouvez facilement l'afficher ou la modifier avec les méthodes habituelles sur les listes. (Vous en apprendrez plus dans le prochain chapitre.)

 
Sélectionnez
>>> import sys                                                 (1)
>>> sys.path                                                   (2)
['', 
 '/usr/lib/python31.zip', 
 '/usr/lib/python3.1',
 '/usr/lib/python3.1/plat-linux2@EXTRAMACHDEPPATH@', 
 '/usr/lib/python3.1/lib-dynload', 
 '/usr/lib/python3.1/dist-packages', 
 '/usr/local/lib/python3.1/dist-packages']
>>> sys                                                        (3)
<module 'sys' (built-in)>
>>> sys.path.insert(0, '/home/mark/diveintopython3/examples')  (4)
>>> sys.path                                                   (5)
['/home/mark/diveintopython3/examples', 
 '', 
 '/usr/lib/python31.zip', 
 '/usr/lib/python3.1', 
 '/usr/lib/python3.1/plat-linux2@EXTRAMACHDEPPATH@', 
 '/usr/lib/python3.1/lib-dynload', 
 '/usr/lib/python3.1/dist-packages', 
 '/usr/local/lib/python3.1/dist-packages']

(1) On importe le module sys pour avoir accès à toutes ses fonctions et attributs.

(2) sys.path est une liste de noms de répertoires dans laquelle Python recherche les bibliothèques. (Vous avez probablement quelque chose de différent ; cela dépend de votre système d’exploitation, de votre version de Python et de son installation d’origine). Python recherche dans ces répertoires (dans l’ordre) un fichier .py dont le nom correspond à ce que vous essayez d’importer.

(3) En fait, j'ai un peu simplifié : c’est un peu plus compliqué que cela car tous les modules ne sont pas des fichiers .py. Certains sont des modules intégrés (built-in modules) ; ils font partie de Python lui-même. Les modules intégrés se comportent comme les autres modules, mais leur code source n’est pas disponible car il n’est pas écrit en Python ! (Comme Python lui-même, ces modules intégrés sont écrits en C).

(4) Vous pouvez ajouter un nouveau répertoire au sys.path et ainsi, Python regardera aussi dans ce répertoire lorsque vous tenterez d’importer un module. L’effet perdure aussi longtemps que Python continue à tourner.

(5) En utilisant sys.path.insert(0, new_path), vous insérez un nouveau répertoire en tête de la liste sys.path, c'est-à-dire au début de la liste des chemins où Python va rechercher des modules. La plupart du temps c’est ce que vous voudrez faire. En cas de conflit de nommage (par exemple, si Python est livré avec la version 2 d’une bibliothèque particulière mais que vous souhaitez utiliser la version 3), ceci assure que vos modules seront trouvés et utilisés au lieu des modules qui ont été installés en même temps que Python.

II-E. Tout est objet

Au cas où vous auriez oublié, je rappelle que les fonctions Python ont des attributs et que ces attributs sont disponibles au moment de l’exécution. Une fonction, comme n’importe quoi d’autre en Python, est un objet.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
>>> import humansize                               ##  (1)
>>> print(humansize.approximate_size(4096, True))  ##  (2)
4.0 KiB
>>> print(humansize.approximate_size.__doc__)      ##  (3)
Convert a file size to human-readable form.

    Keyword arguments:
    size -- file size in bytes
    a_kilobyte_is_1024_bytes -- if True (default), use multiples of 1024
                                if False, use multiples of 1000

    Returns: string

(1) La première ligne importe le programme humansize comme un module — un bout de code que vous pouvez utiliser interactivement, ou dans un programme Python plus gros. Une fois que vous avez importé ce module, vous pouvez faire référence à toutes ses fonctions, classes ou attributs publics et vous pouvez aussi le faire en interactif dans le terminal Python. C’est un concept important et vous en verrez beaucoup dans ce livre.

(2) Quand vous voulez utiliser une fonction définie dans un module importé, vous devez inclure le nom du module. Donc vous ne pouvez pas simplement écrire approximate_size, mais vous devez écrire humansize.approximate_size. Si vous utilisez des classes en Java, ceci devrait vous être vaguement familier.

(3) Au lieu d’appeler la fonction comme vous vous y seriez attendu, vous demandez l’attribut __doc__ de la fonction. 

import en Python agit comme require en Perl. Une fois que vous avez importé un module, vous accédez à ses fonctions avec  module.fonction en Python, et avec module::fonction en Perl.

Absolument tout dans Python est objet et tout objet peut avoir des attributs et des méthodes. Toute fonction a un attribut __doc__ qui retourne sa docstring définie dans le code source de la fonction. Le module sys est un objet qui a (entre autres choses) un attribut nommé path. Et ainsi de suite.

Cependant, ceci ne répond pas à la question la plus fondamentale : qu’est-ce qu’un objet ? Les langages de programmation définissent l’ « objet » de différentes manières. Dans certains langages, tout objet doit avoir des attributs et des méthodes. Dans d’autres, tout objet est dérivable (c'est-à-dire que l’on peut créer une sous-classe de l’objet). En Python la définition est plus vague. Certains objets n’ont ni attribut ni méthode, mais ils pourraient en avoir. Tous les objets ne sont pas dérivables. Cependant tout est un objet dans le sens où l’on peut l’assigner à une variable ou le passer comme argument à une fonction.

Vous avez peut-être entendu le terme « objet de première classe » (first-class object) dans d’autres contextes de programmation. En Python, les fonctions sont des objets de première classe. Vous pouvez passer une fonction en argument à une autre fonction. Les modules sont également des objets de première classe. Vous pouvez passer un module entier en argument à une fonction. Les classes sont des objets de première classe et les instances individuelles d’une classe sont aussi des objets de première classe.

Ceci est important, donc je vais le répéter au cas où vous l’oublieriez : en Python, tout est objet. Les chaînes de caractères sont des objets. Les listes sont des objets. Les fonctions sont des objets. Les classes sont des objets. Les instances de classe sont des objets. Tout module est un objet.

II-F. Indenter son code

Les fonctions Python n’ont pas de mot-clef begin ou end explicite et pas non plus d’accolades pour marquer où démarre la fonction et où elle s’arrête. Le seul délimiteur est un caractère deux-points ( : ) et l’indentation du code lui-même.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
def approximate_size(size, a_kilobyte_is_1024_bytes=True):  ## (1)
    if size < 0:                                            ## (2)
        raise ValueError('number must be non-negative')     ## (3)
                                                            ## (4)
    multiple = 1024 if a_kilobyte_is_1024_bytes else 1000
    for suffix in SUFFIXES[multiple]:                       ## (5) 
        size /= multiple
        if size < multiple:
            return '{0:.1f} {1}'.format(size, suffix)

    raise ValueError('number too large')

(1) Les blocs de code sont définis via leur indentation. Par « bloc de code », j’entends les fonctions, les conditions if, les boucles for et while, etc. Une indentation marque le début d’un bloc, la fin de cette indentation marque la fin du bloc. Il n’y a pas d’accolades explicites, de parenthèses ou de mots-clefs. Ceci signifie que les espaces sont importants et doivent être cohérents. Dans cet exemple, le code de la fonction est indenté avec quatre espaces. Ce n’est pas obligatoirement 4 espaces, il faut juste que les espaces soient cohérents. La première ligne qui n’est pas indentée marque la fin du bloc ou de la fonction.

(2) En Python, une instruction if est suivie d’un bloc de code. Si l’expression du if est évaluée à True, le bloc indenté est exécuté, sinon on passe directement au bloc else (s’il y en a un). Notez qu’il n’y a pas de parenthèses autour de l’expression.

(3) La ligne est à l’intérieur du bloc if. L’instruction raise va lancer une exception (du type ValueError) si size < 0.

(4) Ce n’est pas la fin de la fonction. Les lignes entièrement blanches ne comptent pas. Elles peuvent simplement rendre le code plus lisible et elles ne sont pas considérées comme un délimiteur de bloc de code. La fonction continue sur la ligne suivante.

(5) La boucle for marque également le début d’un bloc de code. Les blocs de code peuvent contenir de multiples lignes tant qu’elles sont indentées de la même manière. Cette boucle for contient trois lignes dans son bloc. Il n’y a pas d’autre syntaxe spécifique pour les blocs de codes sur plusieurs lignes. Vous n’avez qu’à indenter et c’est tout.

Après quelques protestations et analogies méprisantes avec le Fortran, vous ferez la paix avec cela et commencerez à voir l'intérêt de l'indentation. L’un des principaux avantages est que tous les programmes Python se ressemblent, car l’indentation est nécessaire au langage et n’est pas juste une question de style. Ceci rend plus facile la lecture et la compréhension de code Python écrit par d’autres personnes.

Python utilise le retour de chariot (le passage à la ligne) pour séparer les instructions et le caractère deux points et l’indentation pour séparer les blocs. C++ et Java utilisent des points virgules pour séparer les instructions et des accolades pour séparer les blocs.,

II-G. Les exceptions

Les exceptions sont partout en Python. Pratiquement tous les modules de la bibliothèque standard de Python les utilisent et Python lui-même en lance dans beaucoup de circonstances. Nous le verrons à de nombreuses reprises dans ce livre.

Qu’est-ce qu’une exception ? Souvent, c’est une erreur, une indication que quelque chose se passe mal. (Toutes les exceptions ne sont pas des erreurs, mais laissons ceci de côté pour l’instant.) Certains langages de programmation encouragent l’utilisation des codes d’erreur de sortie, que vous pouvez vérifier. Python encourage l’utilisation des exceptions que vous pouvez traiter.

Quand une erreur survient dans le shell Python, il affiche certains détails sur l’exception et comment elle est arrivée. Ceci s'appelle une exception non-gérée. Quand une exception est lancée, il n’y a pas de code pour explicitement l’identifier et la traiter, donc elle remonte jusqu’au haut niveau du shell Python, qui vous affiche des informations de débogage et s'arrête. Une erreur dans le shell ce n’est pas très grave, mais si cela arrive lorsque votre programme Python actuel tourne, le programme entier s’arrêtera abruptement si rien ne traite l’exception. Peut-être que c’est ce que vous voulez, ou peut-être pas.

Contrairement à Java, les fonctions Python ne déclarent pas quelles exceptions elles peuvent lancer. C’est à vous de déterminer quelles sont les exceptions que vous avez besoin d'intercepter.

Une exception n’a cependant pas besoin de provoquer un arrêt complet du programme. Les exceptions peuvent être traitées. Parfois une exception est réelle car vous avez un bogue dans votre code (comme accéder à une variable qui n’existe pas), mais parfois une exception est juste quelque chose que vous pouvez anticiper. Si vous ouvrez un fichier, il peut ne pas exister. Si vous importez un module, il peut ne pas être installé. Si vous vous connectez à une base de données, elle peut être indisponible ou vous pouvez ne pas avoir les droits suffisants pour y accéder. Si vous connaissez une ligne de code qui peut lancer une exception, vous pouvez traiter l’exception en utilisant le bloc try…except.

Python utilise les blocs try…except pour traiter les exceptions et l’instruction raise pour la lancer. Java et C++ utilisent les blocs try…catch pour traiter les exceptions et l’instruction throw pour les générer.

La fonction approximate_size() lance une exception dans deux cas de figure différents : si la taille (size) donnée est plus grande que ce que la fonction est capable de traiter ou bien si elle est inférieure à zéro.

 
Sélectionnez
if size < 0:
    raise ValueError('number must be non-negative')

La syntaxe pour lancer une exception est assez simple. On utilise le mot-clef raise, suivi du nom de l’exception et optionnellement d’une chaîne de caractères lisible pour un humain (une phrase) pour faciliter le débogage. La syntaxe est évocatrice d’un appel de fonction. (En réalité, les exceptions sont implémentées comme des classes et le mot-clef raise crée ici une instance de la classe ValueError et passe la chaîne de caractères « number must be non-negative » à sa méthode d’initialisation. Mais nous verrons cela plus tard !)

Vous n’avez pas besoin de gérer l’exception dans la fonction qui la lance. Si une fonction ne gère pas une exception, alors l’exception est renvoyée à la fonction appelante, qui la renvoie elle-même à sa fonction appelante et ainsi de suite. L’erreur « remonte la pile ». Si une exception n’est jamais gérée, le programme plante et Python affichera un traceback sur la sortie standard et le programme s’arrêtera là. A nouveau, ce n’est peut-être pas ce que vous voulez ; tout dépend de ce que votre programme fait.

II-G-1. Intercepter les erreurs d'importation de module

L’une des exceptions intégrées à Python est l’ImportError, qui est lancée lorsque vous tentez d’importer un module et que cela échoue. Cela peut arriver pour toute une panoplie de raisons, mais, dans le cas le plus simple, c’est lorsque votre module n’existe pas dans votre sys.path. Vous pouvez utiliser cela pour inclure des fonctionnalités optionnelles à votre programme. Par exemple, la librairie chardet fournit un encodeur de chaîne de caractères avec détection automatique. Peut-être que votre programme veut utiliser cette librairie (si elle existe), mais peut-être que vous pourriez vouloir continuer également même si l’utilisateur ne dispose pas de cette librairie. On peut alors faire ceci avec un bloc try…except.

 
Sélectionnez
try:
  import chardet
except ImportError:
  chardet = None

Plus tard dans votre code, vous pourrez vérifier la présence ou non du module chardet avec un simple test conditionnel if .

 
Sélectionnez
if chardet:
  # faire quelque chose
else:
  # continuer tout de même

Un autre cas commun d’utilisation de l’exception ImportError est quand deux modules implémentent une API commune, mais que l’une est préférable (Peut-être est-elle plus rapide ou utilise-t-elle moins de mémoire). Vous pouvez essayer d’importer un module et si cela échoue, vous rabattre sur le second module. Par exemple, dans le chapitre dédié à l’XML, deux modules implémentant une même API – nommée ElementTree – sont présentés. Le premier lxml est un module tiers que l’on a besoin de télécharger et d’installer soi-même. Le second, xml.etree.ElementTree, est plus lent mais il est intégré à la bibliothèque standard de Python 3.

 
Sélectionnez
try:
    from lxml import etree
except ImportError:
    import xml.etree.ElementTree as etree

À la fin du bloc try…except, vous avez importé un module et il se nomme etree. Comme les deux modules implémentent une API commune, le reste de votre code n’a pas besoin de vérifier quel module a été importé. Et comme le module a été importé sous le nom etree dans les deux cas, le reste de votre code n’a plus à être parsemé de clauses if pour appeler des modules aux noms différents.

II-H. Variables non initialisées

Regardons de plus près la ligne de code suivante de la fonction approximate_size() :

 
Sélectionnez
multiple = 1024 if a_kilobyte_is_1024_bytes else 1000

Notez que nous n’avons jamais déclaré la variable multiple, nous lui avons simplement affecté une valeur. Cela suffit, car Python vous le permet. Ce que Python ne vous permettra pas, c’est une référence à une variable qui n’a pas été affectée à une valeur auparavant. Si vous le faites, cela lancera une exception NameError.

 
Sélectionnez
>>> x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
>>> x = 1
>>> x
1

Vous remercierez Python un jour pour cela.

II-I. Tout est sensible à la casse

Tous les noms dans Python sont sensibles à la casse (c'est-à-dire à la distinction entre lettres minuscules et lettres capitales) : les noms des variables, les noms des fonctions, les noms des classes, les noms des modules, les noms des exceptions. Si c'est quelque chose que vous pouvez obtenir, définir, appeler, construire, importer ou lancer, c’est sensible à la casse.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
>>> an_integer = 1
>>> an_integer
1
>>> AN_INTEGER
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'AN_INTEGER' is not defined
>>> An_Integer
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'An_Integer' is not defined
>>> an_inteGer
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'an_inteGer' is not defined

II-J. Lancer un script

Souvenez-vous : en Python tout est objet.

Les modules Python sont des objets et ils ont des attributs bien pratiques. Vous pouvez les utiliser pour tester facilement vos modules au fur et à mesure que vous les écrivez, en incluant un bloc spécial de code qui s’exécute lorsque vous lancez le fichier Python en ligne de commande. Prenons les dernières lignes de humansize.py :

 
Sélectionnez
if __name__ == '__main__':
    print(approximate_size(1000000000000, False))
    print(approximate_size(1000000000000))

Comme en C, Python utilise == pour les comparaisons et = pour l’affectation. Contrairement au C, Python ne supporte pas l’affectation en ligne, donc il n'y a aucune chance d'attribuer accidentellement la valeur que vous pensiez comparer.

Qu'y a-t-il de spécial dans cette condition if ? Et bien, les modules sont des objets et les modules ont un attribut intégré, __name__. Le __name__ d’un module dépend de la manière dont vous utilisez le module. Si vous importez le module, alors __name__ est le nom du fichier du module sans le chemin ni l’extension.

 
Sélectionnez
>>> import humansize
>>> humansize.__name__
'humansize'

Mais vous pouvez aussi exécuter votre module directement en tant que programme autonome. Dans ce cas __name__ aura la valeur spéciale __main__. Ici, Python évaluera alors la condition if, identifiera un test renvoyant vrai et exécutera le code du bloc if, qui dans notre cas, imprime deux valeurs à l’écran.

 
Sélectionnez
c:\home\diveintopython3> c:\python31\python.exe humansize.py
1.0 TB
931.3 GiB

Mais si vous appelez ce script comme un module, la partie contrôlée par la condition if __name__ == '__main:_ ne s'exécutera pas et la fonction renverra l'approximation de la taille qui lui est passée en paramètre.

Et voici votre premier programme Python !

II-K. Pour aller plus loin (en anglais)


précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2019 Mark Pilgrim. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.