from en Python : importer un élément précis d'un module

Le mot-clé from indique d'où vient un import et permet de ne récupérer qu'un élément d'un module, utilisable ensuite sans préfixe.
5 min de lecture
Believemy logo

Définition

Un import classique ramène un module entier sous son nom, qu'il faut ensuite répéter devant chaque élément emprunté. Sur deux lignes, cela ne gêne personne. Quand la même fonction revient quarante fois, le préfixe devient du bruit.

from répond à cet ennui précis : il indique d'où vient l'import, puis laisse choisir ce qu'on en prend. L'élément demandé arrive dans le fichier et s'utilise ensuite sans préfixe.

PYTHON
from datetime import date

# date devient un nom de ce fichier, comme s'il y était défini
aujourdhui = date.today()

Sans from, la même chose demande import datetime puis datetime.date.today(). Sur une ligne, la différence est anecdotique ; sur un fichier entier, le nom du module disparaît de partout sauf de la ligne d'import.


Ce qu'on gagne et ce qu'on perd

Pourquoi ne pas employer partout l'écriture la plus courte ? Parce qu'elle se paie sur les quatre points ci-dessous.

import modulefrom module import x
ÉcriturePlus longue, préfixéePlus courte, directe
Origine visibleOui, à chaque usageNon, seulement en haut du fichier
Risque de collisionFaibleRéel entre deux modules
Bon terrainUn module utilisé partoutUn ou deux éléments précis

La ligne de l'origine est la plus décisive. Avec import module, chaque appel rappelle d'où vient la fonction ; avec from, l'information tient dans une ligne en haut du fichier, ce qui suffit tant que ce fichier reste court.

La collision de noms est l'autre contrepartie. Tant qu'on passe par leur préfixe, deux modules qui exposent une fonction du même nom ne se gênent jamais ; importés avec from, ils se disputent la même place.

Attention

Quand deux imports from ramènent le même nom, le second écrase le premier sans un mot : ni erreur, ni avertissement. Le programme continue, appelle la mauvaise fonction, et le problème n'apparaît que plus tard, loin de sa cause.


La forme à éviter

from module import * ramène d'un coup tout ce que le module expose. L'écriture est séduisante quand on essaie quelque chose vite, et mauvaise dans presque tous les cas.

Le premier dégât touche la lisibilité : plus rien n'indique d'où vient un nom, et le lecteur qui croise join(...) ne sait plus s'il vient de os.path, d'une bibliothèque tierce ou du fichier lui-même. Les outils suivent, puisqu'un vérificateur de types ne résout plus ces noms.

Le second dégât est plus sournois : le jour où le module ajoute un nom, celui-ci débarque chez vous et peut recouvrir une de vos variables, alors que rien n'a changé de votre côté.

PYTHON
# À proscrire : ce qui entre dans le fichier reste une devinette
from os.path import *

# Explicite, donc lisible et vérifiable par les outils
from os.path import join, exists

Un module peut restreindre ce que cette forme expose en déclarant __all__, la liste des noms qu'il accepte de livrer. La précaution atténue le problème sans le supprimer.


Ce qui arrive vraiment en mémoire

Une intuition répandue veut que from datetime import date charge « seulement date », et coûte donc moins qu'un import complet. Ce n'est pas ce qui se passe.

Python exécute le module en entier, comme pour un import ordinaire, puis copie un seul des noms obtenus dans le fichier appelant. La forme choisie ne change donc rien au démarrage ni à la mémoire : elle change ce qui reste visible ensuite.

Cette copie explique un comportement déroutant. Si le module réaffecte plus tard une de ses variables, le nom importé avec from garde la valeur d'origine, quand module.variable verrait la nouvelle. De quoi rendre les rechargements à chaud pénibles à raisonner.

Bon à savoir

Un import intrigue souvent : from __future__ import annotations. Il n'apporte aucune fonction, il active un comportement prévu pour une version ultérieure du langage, et doit précéder toute autre instruction du fichier.


Les imports relatifs

À l'intérieur d'un package, from accepte un chemin relatif écrit avec des points : from .utils import nettoyer désigne un module voisin, from ..core import base remonte d'un niveau.

L'intérêt est de ne pas graver le nom du paquet dans chacun de ses fichiers : renommez-le, les imports internes continuent de fonctionner.

La contrepartie tient au lancement. Ce chemin se calcule à partir du paquet auquel le fichier appartient, information que Python n'a que si le fichier a été atteint comme membre de ce paquet. Exécuter directement un fichier situé au fond de l'arborescence produit alors une ImportError déroutante : « attempted relative import with no known parent package ».

La correction consiste à lancer depuis la racine avec python -m paquet.module. Le même symptôme accompagné d'un ModuleNotFoundError indique plutôt un problème de chemin.


Questions fréquentes

Question

from se met-il forcément en début de ligne ?

Oui, il ouvre l'instruction, et la PEP 8 demande de regrouper ces lignes en haut du fichier. Le mot from apparaît aussi dans raise ValueError(...) from erreur, qui chaîne deux exception pour conserver la cause d'origine : usage sans rapport avec l'import.

Question

Peut-on importer plusieurs éléments d'un coup ?

Oui, en les séparant par des virgules : from math import sqrt, pi, floor. Si la ligne devient trop longue, des parenthèses autour de la liste permettent de l'étaler sur plusieurs lignes, ce que les formateurs font d'eux-mêmes. Mieux vaut cela qu'une ligne par nom.

Question

Comment éviter un conflit de noms ?

Avec as, qui renomme l'élément à l'import : deux fonctions load deviennent load_json et load_csv, et plus rien ne s'écrase. Quand le doute persiste, importer le module entier et garder le préfixe reste la meilleure réponse.

Termes connexes

Découvrez notre glossaire Python

Parcourez les termes et définitions les plus couramment utilisés dans le domaine du développement avec Python.

Partager cet article

Tu veux nous aider ? Fais un lien vers cet article sur tes réseaux ou encore mieux : sur ton site, dans un article ou dans ta newsletter.