J'ai découvert un nouveau motif. Ce modèle est-il bien connu ou quelle est son opinion?
En gros, j’ai du mal à parcourir les fichiers sources pour déterminer les importations de modules disponibles, etc., alors maintenant, au lieu de
import foo
from bar.baz import quux
def myFunction():
foo.this.that(quux)
Je déplace toutes mes importations dans la fonction où elles sont réellement utilisées., Comme ceci:
def myFunction():
import foo
from bar.baz import quux
foo.this.that(quux)
Cela fait quelques choses. Premièrement, je pollue rarement par inadvertance mes modules avec le contenu d’autres modules. Je pourrais définir la variable __all__
pour le module, mais je devrais alors la mettre à jour à mesure que le module évolue, ce qui n'aide pas la pollution de l'espace de noms pour le code qui réside réellement dans le module.
Deuxièmement, je finis rarement avec une litanie d'importations au sommet de mes modules, dont la moitié ou plus dont je n'ai plus besoin parce que je l'ai refactorisé. Enfin, je trouve ce modèle BEAUCOUP plus facile à lire, car chaque nom référencé se trouve exactement dans le corps de la fonction.
La réponse (précédemment) la plus votée à cette question est joliment formatée mais absolument fausse en matière de performances. Laissez-moi démontrer
import random
def f():
L = []
for i in xrange(1000):
L.append(random.random())
for i in xrange(1000):
f()
$ time python import.py
real 0m0.721s
user 0m0.412s
sys 0m0.020s
def f():
import random
L = []
for i in xrange(1000):
L.append(random.random())
for i in xrange(1000):
f()
$ time python import2.py
real 0m0.661s
user 0m0.404s
sys 0m0.008s
Comme vous pouvez le constater, il peut être plus efficace d’importer le module dans la fonction. La raison en est simple. Il déplace la référence d'une référence globale vers une référence locale. Cela signifie que, pour CPython au moins, le compilateur émettra des instructions LOAD_FAST
au lieu de LOAD_GLOBAL
. Celles-ci sont, comme leur nom l'indique, plus rapides. L'autre répondant a gonflé artificiellement le coup de performance de regarder dans sys.modules
de important à chaque itération de la boucle.
En règle générale, il est préférable d’importer par le haut, mais les performances sont non la raison pour laquelle vous accédez au module à de nombreuses reprises. Les raisons sont que l'on peut garder une trace de ce dont dépend un module plus facilement et que cela est cohérent avec la plupart du reste de l'univers Python.
Cela a quelques inconvénients.
Si vous souhaitez tester votre module par le biais d'une modification d'exécution, cela risque de compliquer les choses. Au lieu de faire
import mymodule
mymodule.othermodule = module_stub
Vous devrez faire
import othermodule
othermodule.foo = foo_stub
Cela signifie que vous devrez patcher l'autre module globalement, au lieu de changer simplement ce que la référence dans mymodule indique.
Cela rend évident le type de module dont dépend votre module. Cela est particulièrement irritant si vous utilisez plusieurs bibliothèques tierces ou si vous réorganisez du code.
Je devais conserver un code hérité qui utilisait des importations en ligne partout, ce qui le rendait extrêmement difficile à refactoriser ou à reconditionner.
En raison de la manière dont python met les modules en cache, il n’ya pas de problème de performances. En fait, étant donné que le module se trouve dans l’espace de noms local, l’importation de modules dans une fonction présente un léger avantage en termes de performances.
import random
def f():
L = []
for i in xrange(1000):
L.append(random.random())
for i in xrange(10000):
f()
$ time python test.py
real 0m1.569s
user 0m1.560s
sys 0m0.010s
def f():
import random
L = []
for i in xrange(1000):
L.append(random.random())
for i in xrange(10000):
f()
$ time python test2.py
real 0m1.385s
user 0m1.380s
sys 0m0.000s
Quelques problèmes avec cette approche:
py2exe
, py2app
etc.Donc ... le moyen préféré est de placer toutes les importations en haut du fichier. J'ai constaté que si mes importations devenaient difficiles à suivre, cela signifiait généralement que j'avais trop de code pour qu'il faille mieux le scinder en deux fichiers ou plus.
Certaines situations où je ai ont trouvé des importations dans les fonctions sont utiles:
Aussi: mettre les importations à l'intérieur de chaque fonction est en réalité pas sensiblement plus lent qu'en haut du fichier. La première fois que chaque module est chargé, il est placé dans sys.modules
, et chaque importation ultérieur ne coûte que le temps de rechercher le module, ce qui est assez rapide (il n'est pas rechargé).
Une autre chose utile à noter est que la syntaxe from module import *
à l'intérieur d'une fonction a été supprimée dans Python 3.0.
Vous en trouverez une brève mention sous "Syntaxe supprimée" ici:
Je suggérerais que vous essayiez d'éviter les importations from foo import bar
. Je ne les utilise que dans des packages, où la scission en modules est un détail d'implémentation et il n'y en aura pas beaucoup de toute façon.
Dans tous les autres endroits où vous importez un package, utilisez simplement import foo
, puis faites référence à celui-ci par le nom complet foo.bar
. De cette façon, vous pouvez toujours savoir d'où provient un élément donné et ne pas avoir à gérer la liste des éléments importés (en réalité, ce sera toujours obsolète et les éléments importés ne seront plus utilisés).
Si foo
est un nom très long, vous pouvez le simplifier avec import foo as f
puis écrire f.bar
. Cela reste encore beaucoup plus pratique et explicite que de conserver toutes les importations from
.
Les gens ont très bien expliqué pourquoi il fallait éviter les importations en ligne, mais pas de flux de travail alternatifs pour traiter les raisons pour lesquelles vous les voulez au départ.
J'ai du mal à parcourir les fichiers sources pour déterminer les importations de modules disponibles, etc.
Pour vérifier les importations non utilisées, j'utilise pylint . Il effectue une analyse statique (ish) du code Python, et l'une des (nombreuses) choses qu'il vérifie est les importations inutilisées. Par exemple, le script suivant ..
import urllib
import urllib2
urllib.urlopen("http://stackoverflow.com")
..would générer le message suivant:
example.py:2 [W0611] Unused import urllib2
En ce qui concerne la vérification des importations disponibles, je m'appuie généralement sur l'achèvement (assez simpliste) de TextMate - lorsque vous appuyez sur Échap, il complète le mot actuel avec les autres éléments du document. Si j'ai fait import urllib
, urll[Esc]
passera à urllib
, sinon je saute au début du fichier et ajoute l'importation.
Vous voudrez peut-être jeter un coup d'œil à Import statement overhead dans le wiki python. En bref: si le module a déjà été chargé (regardez sys.modules
), votre code fonctionnera plus lentement. Si votre module n'a pas encore été chargé et que foo
ne sera chargé qu'en cas de besoin, ce qui peut être zéro fois, les performances globales seront meilleures.
Du point de vue des performances, vous pouvez voir ceci: Les instructions d’importation Python doivent-elles toujours figurer en haut d’un module?
En général, je n'utilise que les importations locales pour rompre les cycles de dépendance.
Je crois que cette approche est recommandée dans certains cas/scénarios. Par exemple, dans Google App Engine, le chargement différé de gros modules est recommandé car cela minimisera le coût de réchauffement lié à l'instanciation de nouveaux Python ordinateurs virtuels/interpréteurs. Jetez un œil à une présentation de Google Engine décrivant cela. Cependant, gardez à l'esprit que ne signifie pas , vous devez charger tous vos modules paresseux.