PEP 08 déclare:
Les importations sont toujours placées en haut du fichier, juste après les commentaires et les docstrings de module, et avant les globales et les constantes du module.
Cependant, si la classe / méthode / fonction que j'importe n'est utilisée que dans de rares cas, il est sûrement plus efficace de faire l'importation quand elle est nécessaire?
N'est-ce pas:
class SomeClass(object):
def not_often_called(self)
from datetime import datetime
self.datetime = datetime.now()
plus efficace que ça?
from datetime import datetime
class SomeClass(object):
def not_often_called(self)
self.datetime = datetime.now()
la source
Placer l'instruction import à l'intérieur d'une fonction peut empêcher les dépendances circulaires. Par exemple, si vous avez 2 modules, X.py et Y.py, et qu'ils doivent tous les deux s'importer, cela provoquera une dépendance circulaire lorsque vous importez l'un des modules provoquant une boucle infinie. Si vous déplacez l'instruction import dans l'un des modules, il n'essaiera pas d'importer l'autre module jusqu'à ce que la fonction soit appelée, et ce module sera déjà importé, donc pas de boucle infinie. Lisez ici pour en savoir plus - effbot.org/zone/import-confusion.htm
la source
J'ai adopté la pratique de mettre toutes les importations dans les fonctions qui les utilisent, plutôt qu'en haut du module.
L'avantage que j'obtiens est la capacité de refactoriser de manière plus fiable. Lorsque je déplace une fonction d'un module à un autre, je sais que la fonction continuera de fonctionner avec tout son héritage de test intact. Si j'ai mes importations en haut du module, lorsque je déplace une fonction, je trouve que je passe beaucoup de temps à obtenir les importations du nouveau module complètes et minimales. Un IDE de refactoring pourrait rendre cela non pertinent.
Il y a une pénalité de vitesse comme mentionné ailleurs. J'ai mesuré cela dans ma candidature et l'ai trouvé insignifiant pour mes besoins.
Il est également agréable de pouvoir voir toutes les dépendances de module à l'avance sans avoir recours à la recherche (par exemple grep). Cependant, la raison pour laquelle je me soucie des dépendances de module est généralement parce que j'installe, refactorise ou déplace un système entier comprenant plusieurs fichiers, pas seulement un seul module. Dans ce cas, je vais quand même effectuer une recherche globale pour m'assurer d'avoir les dépendances au niveau du système. Je n'ai donc pas trouvé d'importations mondiales pour m'aider à comprendre un système dans la pratique.
Je mets généralement l'importation de l'
sys
intérieur duif __name__=='__main__'
chèque, puis passe des arguments (commesys.argv[1:]
) à unemain()
fonction. Cela me permet d'utilisermain
dans un contexte oùsys
n'a pas été importé.la source
def main(): print(sys.argv); if True: import sys; main();
Vous devrez encapsulerif __name__=='__main__'
dans une fonction pour créer un nouvel espace de noms.La plupart du temps, cela serait utile pour plus de clarté et raisonnable, mais ce n'est pas toujours le cas. Vous trouverez ci-dessous quelques exemples de circonstances dans lesquelles les importations de modules peuvent vivre ailleurs.
Tout d'abord, vous pourriez avoir un module avec un test unitaire de la forme:
Deuxièmement, vous devrez peut-être importer conditionnellement un module différent lors de l'exécution.
Il existe probablement d'autres situations dans lesquelles vous pouvez placer des importations dans d'autres parties du code.
la source
La première variante est en effet plus efficace que la seconde lorsque la fonction est appelée soit zéro soit une fois. Avec la deuxième invocation et les suivantes, cependant, l'approche "importer chaque appel" est en fait moins efficace. Voir ce lien pour une technique de chargement paresseux qui combine le meilleur des deux approches en effectuant une «importation paresseuse».
Mais il y a des raisons autres que l'efficacité pour lesquelles vous pourriez préférer l'une à l'autre. Une approche est de rendre beaucoup plus clair pour quelqu'un qui lit le code les dépendances de ce module. Ils ont également des caractéristiques de défaillance très différentes - le premier échouera au moment du chargement s'il n'y a pas de module "datetime" tandis que le second n'échouera pas jusqu'à ce que la méthode soit appelée.
Note ajoutée: dans IronPython, les importations peuvent être un peu plus chères qu'en CPython car le code est essentiellement compilé lors de son importation.
la source
Curt fait un bon point: la deuxième version est plus claire et échouera au moment du chargement plutôt que plus tard, et de manière inattendue.
Normalement, je ne m'inquiète pas de l'efficacité du chargement des modules, car c'est (a) assez rapide, et (b) ne se produit généralement qu'au démarrage.
Si vous devez charger des modules lourds à des moments inattendus, il est probablement plus logique de les charger dynamiquement avec la
__import__
fonction, et assurez-vous de détecter lesImportError
exceptions et de les gérer de manière raisonnable.la source
Je ne m'inquiéterais pas trop de l'efficacité du chargement du module à l'avant. La mémoire occupée par le module ne sera pas très grande (en supposant qu'elle soit suffisamment modulaire) et le coût de démarrage sera négligeable.
Dans la plupart des cas, vous souhaitez charger les modules en haut du fichier source. Pour quelqu'un qui lit votre code, il est beaucoup plus facile de savoir quelle fonction ou quel objet provient de quel module.
Une bonne raison d'importer un module ailleurs dans le code est s'il est utilisé dans une instruction de débogage.
Par exemple:
Je pourrais déboguer ceci avec:
Bien sûr, l'autre raison d'importer des modules ailleurs dans le code est si vous devez les importer dynamiquement. C'est parce que vous n'avez pratiquement pas le choix.
Je ne m'inquiéterais pas trop de l'efficacité du chargement du module à l'avant. La mémoire occupée par le module ne sera pas très grande (en supposant qu'elle soit suffisamment modulaire) et le coût de démarrage sera négligeable.
la source
C'est un compromis que seul le programmeur peut décider de faire.
Le cas 1 économise de la mémoire et du temps de démarrage en n'important pas le module datetime (et en faisant l'initialisation qu'il pourrait nécessiter) jusqu'à ce qu'il soit nécessaire. Notez que faire l'importation «uniquement lorsqu'il est appelé» signifie également le faire «à chaque fois appelé», de sorte que chaque appel après le premier entraîne toujours une surcharge supplémentaire liée à l'importation.
Le cas 2 permet d'économiser du temps d'exécution et de la latence en important au préalable datetime afin que not_often_called () revienne plus rapidement lorsqu'il est appelé, et également en n'encourant pas la surcharge d'une importation à chaque appel.
Outre l'efficacité, il est plus facile de voir les dépendances du module à l'avant si les instructions d'importation sont ... à l'avant. Les masquer dans le code peut rendre plus difficile de trouver facilement les modules dont dépend quelque chose.
Personnellement, je suis généralement le PEP, sauf pour des choses comme les tests unitaires et tels que je ne veux pas toujours chargés parce que je sais qu'ils ne seront pas utilisés, sauf pour le code de test.
la source
sys.modules
peut facilement être compensé par les économies réalisées en ne cherchant qu'un nom local au lieu d'un nom global.Voici un exemple où toutes les importations sont au sommet (c'est la seule fois que j'ai besoin de le faire). Je veux pouvoir terminer un sous-processus sur Un * x et Windows.
(En révision: ce que John Millikin a dit.)
la source
C'est comme beaucoup d'autres optimisations - vous sacrifiez une certaine lisibilité pour la vitesse. Comme John l'a mentionné, si vous avez fait vos devoirs de profilage et avez trouvé que c'était un changement suffisamment utile et que vous avez besoin de la vitesse supplémentaire, alors allez-y. Il serait probablement bon de mettre une note avec toutes les autres importations:
la source
L'initialisation du module ne se produit qu'une seule fois - lors de la première importation. Si le module en question provient de la bibliothèque standard, vous pourrez également l'importer à partir d'autres modules de votre programme. Pour un module aussi répandu que datetime, il s'agit également probablement d'une dépendance pour un grand nombre d'autres bibliothèques standard. L'instruction import coûterait alors très peu puisque l'initialisation du module se serait déjà produite. À ce stade, il ne fait que lier l'objet module existant à la portée locale.
Associez ces informations à l'argument de la lisibilité et je dirais qu'il est préférable d'avoir l'instruction import à la portée du module.
la source
Juste pour compléter la réponse de Moe et la question d'origine:
Lorsque nous devons gérer des dépendances circulaires, nous pouvons faire quelques "trucs". En supposant que nous travaillons avec des modules
a.py
etb.py
qui contiennentx()
et by()
, respectivement. Alors:from imports
en bas du module.from imports
fonctions ou méthodes à intérieur qui nécessitent réellement l'importation (ce n'est pas toujours possible, car vous pouvez l'utiliser à plusieurs endroits).from imports
pour être une importation qui ressemble à:import a
Donc, pour conclure. Si vous ne traitez pas des dépendances circulaires et ne faites pas une sorte d'astuce pour les éviter, alors il est préférable de placer toutes vos importations en haut pour les raisons déjà expliquées dans d'autres réponses à cette question. Et s'il vous plaît, lorsque vous faites ces "trucs" avec un commentaire, c'est toujours le bienvenu! :)
la source
Outre les excellentes réponses déjà données, il convient de noter que le placement des importations n'est pas simplement une question de style. Parfois, un module a des dépendances implicites qui doivent d'abord être importées ou initialisées, et une importation de niveau supérieur peut entraîner des violations de l'ordre d'exécution requis.
Ce problème survient souvent dans l'API Python d'Apache Spark, où vous devez initialiser SparkContext avant d'importer des packages ou modules pyspark. Il est préférable de placer les importations pyspark dans une étendue où le SparkContext est garanti d'être disponible.
la source
J'ai été surpris de ne pas voir les chiffres des coûts réels pour les contrôles de charge répétés déjà publiés, bien qu'il existe de nombreuses bonnes explications sur ce à quoi s'attendre.
Si vous importez en haut, vous prenez la charge de charge quoi qu'il arrive. C'est assez petit, mais généralement en millisecondes, pas en nanosecondes.
Si vous importez dans une ou plusieurs fonctions, vous ne prenez le coup pour le chargement que si et quand une de ces fonctions est appelée pour la première fois. Comme beaucoup l'ont souligné, si cela ne se produit pas du tout, vous économisez le temps de chargement. Mais si la ou les fonctions sont souvent appelées, vous prenez un coup répété mais beaucoup plus petit (pour vérifier qu'elle a été chargée; pas pour le recharger réellement). D'autre part, comme l'a souligné @aaronasterling, vous économisez également un peu car l'importation dans une fonction permet à la fonction d'utiliser des recherches de variables locales légèrement plus rapides pour identifier le nom plus tard ( http://stackoverflow.com/questions/477096/python- import-coding-style / 4789963 # 4789963 ).
Voici les résultats d'un test simple qui importe quelques éléments de l'intérieur d'une fonction. Les temps signalés (en Python 2.7.14 sur un Intel Core i7 à 2,3 GHz) sont indiqués ci-dessous (le deuxième appel prenant plus que les appels ultérieurs semble cohérent, mais je ne sais pas pourquoi).
Le code:
la source
Je n'aspire pas à fournir une réponse complète, car d'autres l'ont déjà très bien fait. Je veux juste mentionner un cas d'utilisation lorsque je trouve particulièrement utile d'importer des modules à l'intérieur des fonctions. Mon application utilise des packages et modules python stockés dans certains emplacements en tant que plugins. Au démarrage de l'application, l'application parcourt tous les modules à l'emplacement et les importe, puis elle regarde à l'intérieur des modules et si elle trouve des points de montage pour les plugins (dans mon cas, c'est une sous-classe d'une certaine classe de base ayant une unique ID) il les enregistre. Le nombre de plugins est important (maintenant des dizaines, mais peut-être des centaines à l'avenir) et chacun d'eux est utilisé assez rarement. Avoir des importations de bibliothèques tierces en haut de mes modules de plugin était une petite pénalité lors du démarrage de l'application. En particulier, certaines bibliothèques tierces sont lourdes à importer (par exemple, l'importation de complot essaie même de se connecter à Internet et de télécharger quelque chose qui ajoutait environ une seconde au démarrage). En optimisant les importations (en les appelant uniquement dans les fonctions où elles sont utilisées) dans les plugins, j'ai réussi à réduire le démarrage de 10 secondes à environ 2 secondes. C'est une grande différence pour mes utilisateurs.
Donc ma réponse est non, ne mettez pas toujours les importations en haut de vos modules.
la source
Il est intéressant de noter qu'aucune réponse n'a mentionné le traitement parallèle jusqu'à présent, où il peut être REQUIS que les importations soient dans la fonction, lorsque le code de fonction sérialisé est ce qui est poussé vers d'autres cœurs, par exemple comme dans le cas d'ipyparallel.
la source
Il peut y avoir un gain de performances en important des variables / portée locale à l'intérieur d'une fonction. Cela dépend de l'utilisation de la chose importée dans la fonction. Si vous bouclez plusieurs fois et accédez à un objet global de module, l'importer en tant que local peut vous aider.
test.py
runlocal.py
run.py
Un temps sous Linux montre un petit gain
réel est horloge murale. l'utilisateur est temps dans le programme. sys est temps pour les appels système.
https://docs.python.org/3.5/reference/executionmodel.html#resolution-of-names
la source
Lisibilité
En plus des performances de démarrage, il y a un argument de lisibilité à faire pour localiser les
import
instructions. Par exemple, prenez les numéros de ligne python 1283 à 1296 dans mon premier projet python actuel:Si la
import
déclaration était en haut du fichier, je devrais faire défiler vers le haut, ou appuyer sur Home, pour savoir ce quiET
était. Ensuite, je devrais revenir à la ligne 1283 pour continuer à lire le code.En effet, même si l'
import
instruction était au sommet de la fonction (ou de la classe) comme beaucoup la placeraient, une pagination vers le haut et vers le bas serait nécessaire.L'affichage du numéro de version de Gnome sera rarement fait, donc le
import
début du fichier introduit un retard de démarrage inutile.la source
Je voudrais mentionner un de mes cas d'utilisation, très similaire à ceux mentionnés par @John Millikin et @VK:
Importations facultatives
Je fais l'analyse des données avec Jupyter Notebook, et j'utilise le même bloc-notes IPython comme modèle pour toutes les analyses. Dans certains cas, j'ai besoin d'importer Tensorflow pour effectuer des exécutions de modèle rapides, mais parfois je travaille dans des endroits où tensorflow n'est pas configuré / est lent à importer. Dans ces cas, j'encapsule mes opérations dépendantes de Tensorflow dans une fonction d'assistance, j'importe Tensorflow à l'intérieur de cette fonction et je la lie à un bouton.
De cette façon, je pouvais faire "redémarrer et exécuter tout" sans avoir à attendre l'importation, ni à reprendre le reste des cellules en cas d'échec.
la source
Ceci est une discussion fascinante. Comme beaucoup d'autres, je n'avais même jamais envisagé ce sujet. J'ai été acculé à devoir avoir les importations dans les fonctions à cause de vouloir utiliser Django ORM dans une de mes bibliothèques. Je devais appeler
django.setup()
avant d'importer mes classes de modèle et parce que c'était en haut du fichier, il était glissé dans du code de bibliothèque complètement non Django en raison de la construction de l'injecteur IoC.J'ai en quelque sorte piraté un peu et j'ai fini par mettre le
django.setup()
constructeur singleton et l'importation pertinente en haut de chaque méthode de classe. Maintenant, cela a bien fonctionné, mais cela m'a mis mal à l'aise parce que les importations n'étaient pas au sommet et j'ai également commencé à m'inquiéter du délai supplémentaire des importations. Ensuite, je suis venu ici et j'ai lu avec beaucoup d'intérêt tout le monde à ce sujet.J'ai une longue expérience en C ++ et j'utilise maintenant Python / Cython. Mon point de vue est que pourquoi ne pas mettre les importations dans la fonction à moins que cela ne vous cause un goulot d'étranglement profilé. C'est comme déclarer de l'espace pour les variables juste avant d'en avoir besoin. Le problème est que j'ai des milliers de lignes de code avec toutes les importations en haut! Je pense donc que je vais le faire à partir de maintenant et changer le fichier impair ici et là quand je passerai et que j'aurai le temps.
la source