Pourquoi il faut éviter import * en Python


Ne pas utiliser l’opérateur splat dans un import, vous l’avez sans doute lu 100 fois, mais savez-vous pourquoi ?

Regardez la fonction open :

>>> help(open)
    open(name[, mode[, buffering]]) -> file object
 
    Open a file using the file() type, returns a file object.  This is the
    preferred way to open a file.  See file.__doc__ for further information.
(END)

Maintenant, si j’importe le module os, ça ne change rien :

>>> import os
>>> help(open)
    open(name[, mode[, buffering]]) -> file object
 
    Open a file using the file() type, returns a file object.  This is the
    preferred way to open a file.  See file.__doc__ for further information.
(END)

Si par contre j’importe tout le contenu du module os, sans namespace :

>>> from os import *
>>> help(open)
    open(filename, flag [, mode=0777]) -> fd
 
    Open a file (for low level IO).
(END)

La différence ?

Dans le premier cas, on a la fonction open() built-in de Python. Dans le second cas, la fonction os.open() a été importée et a remplacé la fonction open().

Ici le bug sera très difficile à trouver, car les deux fonctions ont presque la même signature :

open(name[, mode[, buffering]]) -> file object

VS

open(filename, flag [, mode=0777]) -> fd

Et en plus un usage très similaire.

Bottom line, import *, c’est pour les sessions shell. Dans vos fichiers de code, ne l’utilisez pas, vous ne savez pas ce que vous importez.

11 thoughts on “Pourquoi il faut éviter import * en Python

  • nainport

    Bottom line, import *, c’est pour les sessions shell. Dans vos fichiers de code, ne l’utilisez pas, vous ne savez pas ce que vous importez.

    Ou plus exactement, on sait ce que l’on import mais pas ce que ca peut remplacer dans le deja built (built-in + previous imports), non ? :)

  • Jean-Eude

    Du coup, quand t’as fait ‘from os import open’, comment tu peu utiliser le open builtin ?

  • Sam Post author

    @nainport : personnellement je suis incapable de dire ce que j’importe avec *. Par exemple, avec *, tu importe automatiquement le module sys car os import sys. Donc non seulement tu sais pas ce que tu vas remplacer, mais en plus tu ne sais pas ce que tu déclenches derrière.

    @Jean-Eud Tu fais pas from os import open. Mais si tu le faisais, tu pourrais trouver le bout de code responsable facilement. Si tu as des import *, pour trouver d’où vient le problème, bonne chance.

    Pour les imports de la lib standard, on fait presque toujours “import module” de toute façon:

    import re # re.match...
    import sys # sys.path
    import os # os.open
    import urllib2 # urllib2.urlopen
    import uuid # uuid.uuid4
    import json # json.loads
    import pickle # pickle.dumps

    Seuls les modules très spécifiques, avec des noms longs et des fonctions qui ont peu de chance de clasher sont importés avec “from”:

    from itertools import chain
    from datetime import datetime

    Et comme c’est explicite, on sait exactement ce qu’on a dans le namespace si il y a un problème plus tard.

  • Fred

    Ne vaut-il pas mieux faire un import os et ensuite utiliser os.open() chaque fois qu’on en a besoin ?

  • Eric

    Est ce que vous pourriez éclairer ma lanterne à ce sujet.
    Lorsque je dev avec Pygame, je suis obligé de l’importer ainsi :

    import pygame
    from pygame.locals import *

    D’un côté, “.locals”, je ne sais pas du tout à quoi il peut bien servir et d’un autre côté seulement que quasi l’ensemble des programmes utilisant la lib pygame se servent de “from pygame.locals import *”.

  • fspot

    @Jean-Eude : dans un tel cas (à ne pas faire), tu peux retrouver le open builtin via __builtin__.open

  • JoJo

    @Jean-Eude : si tu utilise “from os import open”, tu ne peux plus utiliser le open builtin, mais si tu fais ça, c’est que :
    – tu en es conscient
    – tu veux vraiment utiliser la fonction open de os plutôt que la built-in dans tout ton module

  • Sam Post author

    @Eric : c’est une mauvaise habitude qui a été prise dans beaucoup de programmes pour toolkit graphic (on voit ça aussi dans pas mal de code avec QT ou Tkinter). Tu vois le problème, tu copies cette ligne et tu ne sais pas du tout ce qu’elle fait. Et moi non plus : impossible de savoir les dépendances du programme. Tu en utilises des composants, et tu ne sais pas d’où ils viennent.

    C’est une mauvaise chose, à part dans le cas où tu utilises 20 objets de locals, et ce dans plusieurs fichiers. Ce cas exceptionnel dénote un gros projet et un dev qui sait ce qu’il fait. Dans ce cas le raccourcis est acceptable puisqu’on sait d’où on vient et où on va avec l’expérience, et le bénéfice dépasse le coût.

    Mais un tel dev n’aura pas besoin de mon article et ne le lira pas.

  • G-rom

    Si vraiment tu veux importer open de os et garder le builting open tu peux faire

    from os import open as osopen

Comments are closed.

Des questions Python sans rapport avec l'article ? Posez-les sur IndexError.