Python import style de codage
J'ai découvert un nouveau modèle. Est ce motif bien connu ou qu'est-ce que l'opinion à ce sujet?
En gros, j'ai du mal à scrubbing en haut et en bas des fichiers source pour comprendre ce module, les importations sont disponibles et ainsi de suite, donc maintenant, au lieu de
import foo
from bar.baz import quux
def myFunction():
foo.this.that(quux)
Je déplacer toutes mes importations dans la fonction où elles sont effectivement utilisées., comme ceci:
def myFunction():
import foo
from bar.baz import quux
foo.this.that(quux)
Ce n'est quelques choses. Tout d'abord, j'ai rarement accidentellement polluer mes modules avec le contenu des autres modules. Je pourrais définir le __all__
variable pour le module, mais alors je dois mettre à jour le module évolue, et qui n'aide pas l'espace de noms de la pollution pour le code qui vit en fait dans le module.
Deuxième, j'ai rarement jusqu'à la fin avec une litanie des importations en haut de mes modules, la moitié ou plus de qui je n'ai plus besoin parce que j'ai refait il. Enfin, je trouve ce modèle plus facile à lire, puisque chaque nom de référence est là, dans le corps de la fonction.
- double possible de Devrait Python déclarations d'importation toujours dans le haut d'un module?
Vous devez vous connecter pour publier un commentaire.
La (déjà) haut-voté répondre à cette question est bien mis en forme, mais tout à fait tort sur la performance. Permettez-moi de démontrer
Performance
Haut Importer
D'importation dans le Corps de la Fonction
Comme vous pouvez le voir, il peut être plus efficace pour importer le module de la fonction. La raison pour cela est simple. Il se déplace de référence à partir de l'une des références mondiales pour un local de référence. Cela signifie que, pour Disponible au moins, le compilateur émet
LOAD_FAST
instructions au lieu deLOAD_GLOBAL
instructions. Ce sont, comme le nom l'indique, plus rapide. Les autres répondeur gonflés artificiellement les performances de la recherche ensys.modules
par de l'importation à chaque itération de la boucle.En règle générale, il est préférable d'importer au top mais la performance est pas la raison, si vous accédez au module de beaucoup de temps. Les raisons en sont que l'on peut garder une trace de ce qu'est un module dépend de plus en plus facilement et que cela est compatible avec la plupart du reste de l'Python univers.
LOAD_FAST
pour des raisons de performances,import random
au niveau mondial, puis définirrandom_ = random
à portée locale et l'utilisationrandom_
(ou, mieux encore, de sauver quelque attribut de recherches avecrandom_ = random.random
oufrom random import random
). Manger les performances d'une importation sur chaque appel de fonction pour utiliser lesLOAD_FAST
est une mauvaise idée.Ce ne sont quelques inconvénients.
Tests
Sur la chance, vous souhaitez tester votre module par le biais d'exécution de modification, il peut le rendre plus difficile. Au lieu de faire
Que vous aurez à faire
Cela signifie que vous allez avoir à patcher le othermodule à l'échelle mondiale, plutôt que de simplement changer ce à quoi la référence dans mymodule points à.
Le Suivi Des Dépendances
Cela ne met pas en évidence ce que les modules de votre module dépend. C'est particulièrement irritant si vous utilisez beaucoup de bibliothèques tierces ou sont ré-organisation du code.
Je devais maintenir un code existant utilisé importations inline tous sur la place, il fait le code extrêmement difficile à refactoriser ou de reconditionnement.
Remarques Sur Les Performances
En raison de la façon python caches modules, il n'y a pas de gain de performance. En fait, depuis le module est dans l'espace de noms local, il y a un léger gain de performance de l'importation de modules dans une fonction.
Haut Importer
D'importation dans le Corps de la Fonction
Quelques problèmes avec cette approche:
py2exe
,py2app
etc.Donc... il est préférable de mettre toutes les importations en haut du fichier. J'ai trouvé que si mon importations être dur à suivre, cela signifie généralement que j'ai trop de code que je serais mieux de le diviser en deux ou plusieurs fichiers.
Certaines situations où je ont trouvé des importations à l'intérieur de fonctions utiles:
Aussi: mettre les importations à l'intérieur de chaque fonction est en fait pas sensiblement plus lent qu'en haut du fichier. Le premier temps de chaque module est chargé, il est mis en
sys.modules
, et tous les frais d'importation uniquement le temps de regarder en haut du module, ce qui est relativement rapide (il n'est pas rechargé).Autre chose à noter est que le
from module import *
de la syntaxe à l'intérieur d'une fonction a été supprimée en Python 3.0.Il y a une brève mention de celui-ci en vertu de la "suppression de la Syntaxe" ici:
http://docs.python.org/3.0/whatsnew/3.0.html
Je suggère que vous essayez d'éviter
from foo import bar
importations. Je les utiliser uniquement à l'intérieur des paquets, où le découpage en modules est un détail d'implémentation, et il n'y aura pas beaucoup de toute façon.Dans tous les autres lieux, où l'importation d'un package, il suffit d'utiliser
import foo
puis de référence par le nom completfoo.bar
. De cette façon, vous pouvez toujours dire d'où un certain élément vient et de ne pas avoir à mettre à jour la liste des éléments importés (en réalité, ce sera toujours obsolète et à l'importation n'est plus utilisé des éléments).Si
foo
est vraiment un nom long, vous pouvez simplifier avecimport foo as f
et puis écriref.bar
. C'est encore bien plus pratique et explicite que le maintien de toutes lesfrom
importations.Les gens ont très bien expliqué pourquoi, afin d'éviter les importations, mais pas vraiment d'alternative flux de travail pour aborder les raisons pour lesquelles vous voulez en premier lieu.
À vérifier pour les importations-je utiliser pylint. Il ne statique(ish)-analyse de code Python, et l'une des (nombreuses) choses, il vérifie est pas utilisé importations. Par exemple, le script suivant..
..générera le message suivant:
Comme pour la vérification de la disposition des importations, en général, je compter sur TextMate est (assez simpliste) de l'achèvement lorsque vous appuyez sur la touche Echap, il complète le mot en cours avec d'autres personnes dans le document. Si j'ai fait
import urllib
,urll[Esc]
étendra àurllib
, sinon je saute au début du fichier et ajoutez le à l'importation.À partir d'un point de vue des performances, vous pouvez voir ceci: Devrait Python déclarations d'importation toujours dans le haut d'un module?
En général, je n'utilise que des locaux des importations dans le but de briser la dépendance des cycles.
Vous voudrez peut-être jeter un coup d'oeil à l'Importation relevé de frais généraux dans le python wiki. En bref: si le module a déjà été chargé (regardez
sys.modules
) votre code va s'exécuter plus lentement. Si votre module n'a pas encore été chargé, etfoo
ne se charge lorsque nécessaire, ce qui peut être zéro fois, puis l'ensemble de la performance sera meilleure.Je crois que c'est une approche recommandée dans certains cas/scénarios. Par exemple dans Google App Engine chargement différé gros modules est recommandé, car il permettra de minimiser l'échauffement coût de l'instanciation de Python VMs/interprètes. Jetez un oeil à un Google de l'Ingénieur présentation décrivant cette. Cependant, gardez à l'esprit ce n'est pas dire que vous devriez paresseux-charge tous vos modules.
Les deux variantes ont leurs usages. Cependant, dans la plupart des cas, il est préférable d'importer en dehors des fonctions, et non pas à l'intérieur d'eux.
Performance
Il a été mentionné dans plusieurs réponses, mais à mon avis, ils manquent d'une discussion complète.
La première fois qu'un module est importé dans un interpréteur python il sera lente, peu importe si c'est dans le premier niveau, ou à l'intérieur d'une fonction. C'est lent parce que Python (je me concentre sur Disponible, il pourrait être différent pour d'autres implémentations de Python) n'plusieurs étapes:
__pycache__
répertoire ou le.pyx
fichiers) et si non il convertit ces vers du bytecode.sys.les modules
.Les importations ultérieures de ne pas avoir à faire toutes ces parce que Python peut simplement renvoyer le module de
sys.les modules
. Donc, pour les importations ultérieures seront beaucoup plus rapides.Il se pourrait qu'une fonction dans votre module n'est pas utilisé très souvent, mais elle dépend d'un
import
qui est de prendre assez longue. Ensuite, vous pouvez effectivement déplacer leimport
l'intérieur de la fonction. Que va faire l'importation de votre module plus rapide (car il n'a pas à importer le long du chargement du package immédiatement) toutefois, lorsque la fonction est enfin utilisé, il sera lent sur le premier appel (car alors le module doit être importé). Qui peuvent avoir un impact sur la perception de la performance, parce qu'au lieu de ralentir tous les utilisateurs que vous ralentir uniquement ceux qui utilisent la fonction qui dépend de la lenteur de chargement de la dépendance.Cependant, la recherche dans
sys.modules
n'est pas gratuit. Il est très rapide, mais ce n'est pas gratuit. Donc, si vous avez réellement appeler une fonction quiimport
s un package très souvent, vous remarquerez une légère dégradation des performances:Qui est presque deux fois plus lent.
Il est très important de réaliser que aaronasterling "triché" un peu dans la réponse. Il a déclaré que de procéder à l'importation dans la fonction rend la fonction plus rapide. Et dans une certaine mesure, cela est vrai. C'est parce que la façon Python regarde les noms:
Donc, au lieu de vérifier l'étendue locale et puis vérification de la portée globale, il suffit de cocher la portée locale parce que le nom du module est disponible dans le domaine local. Qui le rend effectivement plus rapide! Mais c'est une technique appelée "Boucle d'extraction de code invariant". Cela signifie essentiellement que vous réduire les frais généraux de quelque chose qui se fait dans une boucle (ou plusieurs fois), en les stockant dans une variable avant la boucle (ou les appels répétés). Ainsi, au lieu de
import
ing dans la fonction vous pouvez aussi tout simplement utiliser une variable et l'affecter au nom global:Alors que vous pouvez clairement voir que les répétée des recherches pour le mondial
random
sont lentes, il n'y a pratiquement pas de différence entre l'importation du module à l'intérieur de la fonction ou de la cession du module global dans une variable à l'intérieur de la fonction.Cela pourrait être poussée à l'extrême en évitant la fonction de recherche à l'intérieur de la boucle:
Encore une fois beaucoup plus rapide, mais il n'y a presque pas de différence entre l'importation et la variable.
Dépendances optionnelles
Parfois, avoir un niveau module importation peut être un problème. Par exemple, si vous ne voulez pas d'ajouter une autre installation de la dépendance, mais le module serait vraiment utile pour certains supplémentaires fonctionnalité. Décider si une dépendance devrait être facultatif ne devrait pas être fait à la légère, car il aura une incidence sur les utilisateurs (soit, si elles reçoivent une inattendue
ImportError
ou autrement manquer les "fonctionnalités") et il permet d'installer le paquet avec toutes les fonctionnalités plus complexes, normal dépendancespip
ouconda
(pour n'en citer que deux gestionnaires de paquets) travailler hors de la boîte, mais pour les dépendances optionnelles, les utilisateurs doivent installer des paquets manuellement plus tard (il y a quelques options qui permettent de personnaliser les exigences, mais là encore le fardeau de l'installation "correctement" est mis sur l'utilisateur).Mais encore une fois cela peut être effectué de deux manières:
ou:
Cela pourrait être plus personnalisé en fournissant des implémentations alternatives ou la personnalisation de l'exception (ou un message), l'utilisateur voit, mais c'est la principale essentiel.
Haut-niveau l'approche pourrait être un peu mieux si l'on veut proposer une "solution" à la dépendance optionnelle, mais en générale, les gens utilisent la fonction d'importation. Surtout parce qu'il conduit à un nettoyeur stacktrace et est plus courte.
Circulaire Importations
En Fonction des importations peut être très utile pour éviter ImportErrors en raison de la circulaire des importations. Dans de nombreux cas, la circulaire importations sont un signe de "mauvaise" ensemble de la structure, mais si il n'y a absolument aucun moyen d'éviter une circulaire à l'importation du "cercle" (et donc les problèmes) sont résolus en mettant les importations d'entraîner le cercle à l'intérieur les fonctions qui l'utilisent en fait.
Ne vous répétez pas
Si vous avez réellement mettre toutes les importations dans la fonction au lieu de la portée de module vous permettra d'introduire de la redondance, car il est probable que les fonctions requièrent la même importations. Qui a quelques inconvénients:
Réflexions supplémentaires:
La plupart des IDEs ont déjà un pion pour les importations, de sorte que c'est probablement juste quelques clics pour les supprimer. Même si vous n'utilisez pas un IDE, vous pouvez utiliser un code statique script de vérification de temps en temps et de le corriger manuellement. Une autre réponse mentionné pylint, mais il en existe d'autres (par exemple pyflakes).
C'est pourquoi vous utilisez généralement
__all__
et/ou de définir vos fonctions submodules et d'importer les classes/fonctions/... dans le module principal, par exemple le__init__.py
.Aussi, si vous pensez que vous pollué le module de l'espace de noms trop, alors vous devriez probablement envisager de diviser le module dans submodules, cependant, qui n'a de sens que pour des dizaines d'importations.
Un autre (très important) le point de le mentionner si vous souhaitez réduire la pollution de l'espace de noms est en évitant une
from module import *
importations. Mais vous pouvez également vouloir éviterfrom module import a, b, c, d, e, ...
importations d'importation trop noms et il suffit d'importer le module d'accès et les fonctions avecmodule.c
.Comme un dernier recours, vous pouvez toujours utiliser des alias pour éviter de polluer l'espace de noms avec "public" importations en utilisant:
import random as _random
. Que va rendre le code plus difficile à comprendre, mais il est très clair ce que doit être visible du grand public et ce qui ne l'est pas. Ce n'est pas quelque chose que je recommanderais , vous devriez juste garder la__all__
liste à jour (ce qui est fortement recommandé et est sensible à l'approche).Résumé
L'impact sur les performances est visible, mais presque toujours, il sera de la micro-optimisation, afin de ne pas laisser la décision dans le cas où vous mettez les importations être guidée par des micro-benchmarks. Sauf si la dépendance est vraiment lent sur le premier
import
et il n'est utilisé que pour un petit sous-ensemble de la fonctionnalité. Alors qu'il peut réellement avoir un impact visible sur la perception de la performance de votre module pour la plupart des utilisateurs.Utiliser couramment compris des outils pour la définition de l'API publique, je veux dire la
__all__
variable. Il pourrait être un peu gênant de le tenir à jour mais c'est la vérification de toutes les fonctions obsolètes les importations ou lorsque vous ajoutez une nouvelle fonction pour ajouter toutes les importations dans cette fonction. Dans le long terme, vous aurez probablement à faire moins de travail par la mise à jour__all__
.Il n'a vraiment pas d'importance que celle que vous préférez, les deux fonctionnent. Si vous travaillez seul, on peut raisonner sur les avantages et les inconvénients et de faire ce que vous en pensez est le meilleur. Toutefois, si vous travaillez dans une équipe, vous devez probablement tenir à connu-modèles (ce qui serait top au niveau des importations,
__all__
), car il leur permet de faire ce qu'ils ont (probablement) a toujours fait.Implémentations De Sécurité
Envisager un environnement où tous de votre code Python est situé à l'intérieur d'un dossier privilégié l'utilisateur a accès. Afin d'éviter l'exécution de l'ensemble de votre programme utilisateur privilégié, vous décidez d'abandonner des privilèges à un utilisateur non privilégié au cours de l'exécution. Dès que vous utilisez une fonction que les importations d'un autre module, votre programme va jeter un
ImportError
depuis l'utilisateur non privilégié est impossible d'importer le module en raison des autorisations de fichier.