web-dev-qa-db-fra.com

Pourquoi "import *" est-il mauvais?

Il est recommandé de ne pas utiliser import * en Python. 

Quelqu'un peut-il s'il vous plaît partager la raison de cela, afin que je puisse éviter de le faire la prochaine fois?

121
  • Parce qu'il met beaucoup de choses dans votre espace de noms (peut masquer un autre objet de l'importation précédente et vous ne le saurez pas). 

  • Parce que vous ne savez pas exactement ce qui est importé et que vous ne pouvez pas trouver facilement de quel module une certaine chose a été importée (lisibilité). 

  • Parce que vous ne pouvez pas utiliser des outils tels que pyflakes pour détecter de manière statique les erreurs dans votre code.

186
gruszczy

Selon le Zen de Python :

Explicite est meilleur qu'implicite.

... ne peut pas discuter avec ça, sûrement?

40
Federer

Vous ne passez pas **locals() aux fonctions, n'est-ce pas?

Comme Python manque d'une instruction "include", et le paramètre self est explicite, les règles et / sont assez simples, il est généralement très facile de pointer du doigt une variable et d'indiquer d'où provient l'objet - - sans lire d'autres modules et sans aucune sorte de IDE (qui sont de toute façon limités en termes d'introspection, du fait que le langage est très dynamique).

Le import * casse tout ça.

En outre, il a une possibilité concrète de cacher des insectes.

import os, sys, foo, sqlalchemy, mystuff
from bar import *

Désormais, si le module de barres possède l’un des attributs "os", "mystuff", etc ..., il remplacera les attributs explicitement importés et peut éventuellement indiquer des éléments très différents. Il est souvent judicieux de définir __all__ dans la barre - cela indique ce qui sera implicitement importé - mais il est toujours difficile de retracer la provenance des objets, sans lire et analyser le module de barre et suivre ses imports. Un réseau de import * est la première chose que je corrige lorsque je prends possession d'un projet.

Ne me comprenez pas mal: si le import * manquait, je pleurerais de l'avoir. Mais il faut l'utiliser avec précaution. Un bon cas d'utilisation consiste à fournir une interface de façade sur un autre module . De même, l'utilisation d'instructions d'importation conditionnelles, ou d'importations à l'intérieur d'espaces de noms fonction/classe, nécessite un peu de discipline.

Je pense que dans les projets de moyenne à grande taille, ou dans les petites avec plusieurs contributeurs, un minimum d’hygiène est nécessaire en termes d’analyse statique - exécuter au moins pyflakes ou mieux un pylint bien configuré - pour attraper plusieurs types de bugs avant. ils se produisent.

Bien sûr, puisqu'il s'agit de python - n'hésitez pas à enfreindre les règles et à explorer - mais méfiez-vous des projets qui pourraient se multiplier par dix, si le code source manque de discipline, ce sera un problème.

33
Marco Mariani

C'est parce que vous polluez l'espace de noms. Vous allez importer toutes les fonctions et les classes dans votre propre espace de noms, ce qui peut entrer en conflit avec les fonctions que vous définissez vous même.

De plus, je pense que l'utilisation d'un nom qualifié est plus claire pour la tâche de maintenance; vous voyez sur la ligne de code elle-même d'où provient une fonction, ce qui vous permet de consulter la documentation beaucoup plus facilement.

Dans le module foo:

def myFunc():
    print 1

Dans votre code:

from foo import *

def doThis():
    myFunc() # Which myFunc is called?

def myFunc():
    print 2
15
extraneon

from ... import * est acceptable dans une session interactive.

14
codeape

http://docs.python.org/tutorial/modules.html

Notez qu'en général, la pratique d'importer * à partir d'un module ou d'un package est mal vue, car elle provoque souvent un code peu lisible

11
Felix Kling

Disons que vous avez le code suivant dans un module appelé foo:

import ElementTree as etree

et ensuite dans votre propre module vous avez:

from lxml import etree
from foo import *

Vous avez maintenant un module difficile à déboguer qui ressemble à il contient l'etree de lxml, mais qui contient réellement ElementTree à la place.

7
jcdyer

Ce sont toutes de bonnes réponses. Je vais ajouter que pour enseigner à de nouvelles personnes à coder en Python, traiter avec import * est très difficile. Même si vous ou eux n'ont pas écrit le code, cela reste une pierre d'achoppement.

J'apprends aux enfants (environ 8 ans) à programmer en Python pour manipuler Minecraft. J'aime leur fournir un environnement de codage utile avec lequel travailler ( Éditeur Atom ) et enseigner le développement piloté par REPL (via bpython ). Dans Atom, je trouve que les astuces/achèvements fonctionnent aussi efficacement que bpython. Heureusement, contrairement à d'autres outils d'analyse statistique, Atom n'est pas dupe de import *.

Cependant, prenons cet exemple ... Dans cet encapsuleur ils from local_module import * un tas de modules incluant cette liste de blocs . Ignorons le risque de collisions entre espaces de noms. En faisant from mcpi.block import *, ils font de cette liste complète de types de blocs obscurs quelque chose que vous devez aller voir pour savoir ce qui est disponible. S'ils avaient plutôt utilisé from mcpi import block, vous pouvez taper walls = block. et une liste de saisie semi-automatique apparaîtra alors .  Atom.io screenshot

6
Bruno Bronosky

Compris les points valides que les gens mettent ici. Cependant, j’ai un argument selon lequel, parfois, "importer des étoiles" peut ne pas toujours être une mauvaise pratique:

  • Lorsque je veux structurer mon code de manière à ce que toutes les constantes soient dirigées vers un module appelé const.py:
    • Si je fais import const, alors pour chaque constante, je dois le désigner par const.SOMETHING, ce qui n’est probablement pas le moyen le plus pratique.
    • Si je fais from const import SOMETHING_A, SOMETHING_B ..., alors évidemment c'est beaucoup trop verbeux et va à l'encontre du but de la structuration.
    • Ainsi, je pense que dans ce cas, un from const import * peut être un meilleur choix.
4
ibic

En guise de test, j'ai créé un module test.py avec 2 fonctions A et B, qui affichent respectivement "A 1" et "B 1". Après avoir importé test.py avec:

import test

. . . Je peux exécuter les 2 fonctions en tant que test.A () et test.B (), et "test" s'affiche sous la forme d'un module dans l'espace de noms. Par conséquent, si je modifie test.py, je peux le recharger avec:

import importlib
importlib.reload(test)

Mais si je fais ce qui suit:

from test import *

il n'y a aucune référence à "test" dans l'espace de noms, il n'y a donc aucun moyen de le recharger après une modification (autant que je sache), ce qui pose problème dans une session interactive. Considérant que l’un des éléments suivants:

import test
import test as tt

ajoutera "test" ou "tt" (respectivement) en tant que noms de modules dans l’espace de nommage, ce qui permettra le rechargement.

Si je fais:

from test import *

les noms "A" et "B" apparaissent dans l'espace de noms sous la forme functions. Si je modifie test.py et répète la commande ci-dessus, les versions modifiées des fonctions ne sont pas rechargées.

Et la commande suivante provoque un message d'erreur.

importlib.reload(test)    # Error - name 'test' is not defined

Si quelqu'un sait comment recharger un module chargé avec "from module import *", merci de poster. Sinon, ce serait une autre raison d'éviter le formulaire:

from module import *
2
Alex

C'est un très MAUVAIS pratique pour deux raisons:

  1. Lisibilité du code
  2. Risque de dépasser les variables/fonctions, etc.

Pour point 1 : Voyons un exemple de ceci:

from module1 import *
from module2 import *
from module3 import *

a = b + c - d

Ici, en voyant le code, personne ne saura à quel module b, c et d appartient réellement. 

Par contre, si vous le faites comme:

#                   v  v  will know that these are from module1
from module1 import b, c   # way 1
import module2             # way 2

a = b + c - module2.d
#            ^ will know it is from module2

C'est beaucoup plus propre pour vous, et la nouvelle personne qui rejoint votre équipe aura une meilleure idée.

Pour point 2 : Disons que module1 et module2 ont pour variable b. Quand je fais:

from module1 import *
from module2 import *

print b  # will print the value from module2

Ici, la valeur de module1 est perdue. Il sera difficile de déboguer pourquoi le code ne fonctionne pas même si b est déclaré dans module1 et que j'ai écrit le code dans l'attente d'utiliser mon code module1.b

Si vous avez les mêmes variables dans différents modules et que vous ne souhaitez pas importer le module en entier, vous pouvez même faire:

from module1 import b as mod1b
from module2 import b as mod2b
2
Moinuddin Quadri

Comme suggéré dans la documentation, vous ne devez jamais utiliser import * dans le code de production.

Une autre raison à éviter est que importer * depuis un package fonctionne différemment de import * depuis un module, ce qui peut entraîner des erreurs. Fondamentalement, from package import * importe tous les noms définis par __init__.py, mais il inclut également tous les sous-modules du package explicitement chargés par les instructions d'importation précédentes. Considérons ce code:

# anywhere in the code before import *
import sound.effects.echo
import sound.effects.surround

# in your module
from sound.effects import *

Dans cet exemple, les modules echo et surround sont importés dans l'espace de noms actuel (en remplaçant éventuellement les définitions précédentes) car ils sont définis dans le package sound.effects lors de l'exécution de l'instruction from sound.effects import *.

0
Eugene Yarmash