Définition
Au début, un programme tient dans un seul fichier. Puis il grossit, une fonction utile mériterait de servir ailleurs, et surtout l'essentiel de ce dont vous avez besoin a déjà été écrit par d'autres. Recopier ce code dans chaque projet serait absurde : autant de copies, autant de versions à corriger.
import répond exactement à cet ennui. L'instruction va chercher un module, c'est-à-dire un fichier Python écrit ailleurs, et rend son contenu utilisable ici. Ce module peut venir de la bibliothèque standard livrée avec Python, d'un package installé, ou d'un autre fichier de votre projet : la même ligne sert dans les trois cas.
# json est livré avec Python, il n'y a rien à installer
import json
# Le nom du module devient le préfixe de tout ce qu'il contient
donnees = json.loads(reponse)Ce préfixe n'est pas une contrainte, c'est un repère. En croisant json.loads au milieu de trois cents lignes, on sait d'où vient la fonction sans remonter en haut du fichier ni ouvrir la documentation.
Les trois écritures
La ligne d'import s'écrit de trois manières, et ce choix décide de ce que vous manipulez dans tout le reste du fichier.
| Écriture | Ce qu'elle donne |
|---|---|
import json | Le module entier, accessible via json. |
from json import loads | Une seule fonction, accessible sans préfixe |
import numpy as np | Le module entier, sous un nom raccourci |
La première garde l'origine visible partout, au prix d'un préfixe répété. La deuxième ramène un seul élément et allège l'écriture, mais l'origine ne tient plus que dans la ligne d'import. La troisième renomme au passage : np pour numpy ou pd pour pandas sont des conventions si répandues qu'elles se lisent comme le nom complet.
Une quatrième forme existe, from module import *, qui déverse tout dans l'espace courant. Elle est déconseillée sans exception : plus rien n'indique d'où vient un nom, et deux modules qui exposent le même nom s'écrasent silencieusement, sans erreur ni avertissement.
Où Python va chercher
Quand vous écrivez import json, Python ne devine rien : il parcourt une liste de dossiers dans un ordre fixe et s'arrête au premier qui porte ce nom. Le dossier du fichier lancé vient d'abord, puis les chemins de l'environnement, puis les paquets installés.
Cet ordre explique la plupart des ModuleNotFoundError, mais aussi une famille de bugs autrement incompréhensibles. Puisque le dossier courant passe en premier, un de vos fichiers nommé comme une bibliothèque connue prend la place de celle-ci.
Un fichier json.py posé dans un projet détourne vers lui tous les import json du dossier, bibliothèque standard comprise. Les erreurs qui suivent parlent d'un attribut manquant, jamais du nom du fichier, et la piste peut se chercher une heure.
L'environnement compte autant que le nom. Un paquet installé avec pip n'existe que dans l'environnement virtuel où l'installation a eu lieu. Un import qui fonctionnait hier et échoue aujourd'hui vient souvent de là : ce n'est pas le code qui a changé, c'est l'interpréteur qui lance le script.
Ce qui se passe au premier import
Un import ne se contente pas de rendre un nom disponible : Python ouvre le fichier du module, l'exécute de haut en bas une seule fois, puis range le résultat dans un cache.
La conséquence surprend souvent : tout ce qu'un module écrit à son niveau supérieur s'exécute chez celui qui l'importe. Un print laissé après un test, une connexion ouverte, une longue préparation de données, tout part à l'import. C'est pour cette raison qu'un fichier destiné à être exécuté range son code de lancement derrière un point d'entrée, la fameuse ligne if __name__ == "__main__":.
Le cache, lui, rend les imports répétés gratuits. Un module importé dans dix fichiers n'est chargé qu'une fois par exécution. Multiplier les lignes d'import ne coûte donc rien en performance, seulement en lisibilité le jour où elles ne servent plus.
Ce cache déroute en session interactive : modifier un module puis relancer son import ne change rien, Python le voit déjà chargé et ne relit pas le fichier. Il faut redémarrer la session, ou passer par importlib.reload.
Où placer ses imports
La PEP 8 demande de rassembler tous les imports en haut du fichier, groupés en trois blocs séparés par une ligne vide : bibliothèque standard, paquets tiers, puis modules du projet. L'intérêt est de voir en cinq secondes ce dont ce fichier dépend, sans le parcourir.
Deux situations justifient de descendre un import dans le code. Rompre une dépendance circulaire, quand deux modules s'importent mutuellement sans qu'aucun parvienne à finir de se charger. Et différer un chargement coûteux, pour ne le payer que si la branche concernée est empruntée.
Questions fréquentes
Pourquoi Python ne trouve-t-il pas mon module ?
Le message ModuleNotFoundError donne le nom exact recherché, et l'explication est presque toujours l'une des trois suivantes : le paquet n'est pas installé dans l'environnement actif, le nom d'installation diffère du nom d'import (pip install pillow donne import PIL), ou votre fichier se trouve dans un dossier que Python n'explore pas.
Faut-il importer tout le module ou seulement ce qu'on utilise ?
Importer le module entier garde l'origine sous les yeux à la lecture, ce qui vaut cher sur un fichier long. Importer une fonction précise allège l'écriture quand elle revient vingt fois. Les deux sont corrects, et rester cohérent dans un même fichier compte davantage que le choix.
Un import peut-il ralentir le démarrage ?
Oui, certaines bibliothèques de calcul mettent près d'une seconde à se charger, et cette seconde est payée avant la première ligne utile. Sur un script en ligne de commande qui doit répondre vite, déplacer un import lourd à l'intérieur de la fonction qui en a besoin devient une optimisation légitime.