Devrait déclarations d'importation toujours dans le haut d'un module?

PEP 08 states:

les importations sont toujours placées en haut du fichier, juste après les commentaires et les docstrings de n'importe quel module, et avant les modules globals et les constantes.

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()
313
demandé sur martineau 2008-09-24 21:21:47
la source

17 ответов

L'importation de modules

est assez rapide, mais pas instantanée. Cela signifie que:

  • placer les importations au sommet du module est très bien, parce que c'est un coût insignifiant qui n'est payé qu'une fois.
  • mettre les importations dans une fonction fera que les appels à cette fonction prendront plus de temps.

donc si vous vous souciez de l'efficacité, mettez les importations au sommet. Seulement les déplacer dans une fonction si votre profilage montre que vous aider (vous did profil pour voir où le meilleur pour améliorer la performance, non??)


Les bonnes raisons que j'ai vu à effectuer paresseux importations sont:

  • en Option support de bibliothèque. Si votre code a plusieurs chemins qui utilisent des bibliothèques différentes, ne vous brisez pas si une bibliothèque optionnelle n'est pas installée.
  • dans le __init__.py d'un plugin, qui pourrait être importé mais pas effectivement utilisés. Des exemples sont les plugins Bazaar, qui utilisent le cadre de chargement paresseux de bzrlib .
219
répondu John Millikin 2008-09-24 21:38:00
la source

mettre la déclaration d'importation à 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 un des modules provoquant une boucle infinie. Si vous déplacez la déclaration d'importation dans l'un des modules alors 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. Lire ici pour plus - effbot.org/zone/import-confusion.htm

67
répondu Moe 2017-06-15 17:50:16
la source

j'ai adopté la pratique de mettre toutes les importations dans les fonctions qui les utilisent, plutôt qu'au sommet du module.

l'avantage que j'obtiens est la possibilité de me reformuler de façon plus fiable. Quand 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 mon importations en haut du module, lorsque je déplace une fonction, je trouve que j'ai passé beaucoup de temps à apprendre la nouvelle les importations du module sont complètes et minimales. Un IDE de reconfiguration pourrait rendre cela hors de propos.

il y a une pénalité de vitesse comme mentionné ailleurs. Je l'ai mesuré dans ma demande et je l'ai trouvé insignifiant à mes fins.

il est également agréable de pouvoir voir toutes les dépendances des modules à l'avance sans recourir à la recherche (par exemple grep). Cependant, la raison pour laquelle je me soucie des dépendances de module est généralement parce que je suis l'installation, le remaniement, ou le déplacement un système complet comprenant plusieurs fichiers, pas un seul module. Dans ce cas, je vais effectuer une recherche globale de toute façon pour m'assurer que j'ai 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 place habituellement l'importation de sys à l'intérieur du if __name__=='__main__' check Et puis passer des arguments (comme sys.argv[1:] ) à une fonction main() . Ceci me permet d'utiliser main dans un contexte où sys n'a pas été importé.

50
répondu 2008-09-24 22:16:13
la source

la Plupart du temps ce serait utile pour la clarté et raisonnable de le faire, mais il n'est pas toujours le cas. Voici quelques exemples de circonstances dans lesquelles les importations de modules pourraient vivre ailleurs.

tout D'abord, vous pourriez avoir un module avec un test unitaire de la forme:

if __name__ == '__main__':
    import foo
    aa = foo.xyz()         # initiate something for the test

Deuxièmement, vous pourriez avoir l'obligation d'importer conditionnellement un autre module à l'exécution.

if [condition]:
    import foo as plugin_api
else:
    import bar as plugin_api
xx = plugin_api.Plugin()
[...]

Il ya probablement d'autres situations où vous pourriez placer les importations dans d'autres parties du code.

31
répondu ConcernedOfTunbridgeWells 2011-08-19 18:59:06
la source

La première variante est en effet plus efficace que la deuxième, lorsque la fonction est appelée zéro ou une fois. Avec la deuxième invocation et les suivantes, cependant, l'approche "import every call" est en fait moins efficace. Voir ce lien pour une technique de chargement paresseux qui combine le meilleur des deux approches en faisant une"importation paresseux".

mais il y a des raisons autres que l'efficacité pour lesquelles vous pourriez préférer l'un à l'autre. Un l'approche est rend beaucoup plus clair pour quelqu'un lisant le code quant aux dépendances que ce module a. Elles ont aussi des caractéristiques de défaillance très différentes : la première se ratera au moment de la charge s'il n'y a pas de module "datetime", tandis que la seconde ne se ratera pas jusqu'à ce que la méthode soit appelée.

Ajout d'une Remarque: En IronPython, les importations peuvent être tout à fait un peu plus cher que dans Disponible car le code est fondamentalement compilé comme il est en cours d'importation.

14
répondu Curt Hagenlocher 2008-09-24 22:02:25
la source

Curt fait un bon point: la deuxième version est plus claire et échouera au moment de la charge 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 il est (a) assez rapide, et (B) la plupart du temps ne se produit 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 fonction __import__ , et être sûr d'attraper ImportError des exceptions, et de les gérer d'une manière raisonnable.

8
répondu Dan Lenski 2008-09-24 21:32:50
la source

Je ne me soucierais pas trop de l'efficacité du chargement du module à l'avance. La mémoire absorbée par le module ne sera pas très importante (en supposant qu'il soit suffisamment modulaire) et le coût de démarrage sera négligeable.

dans la plupart des cas, vous voulez charger les modules en haut du fichier source. Pour quelqu'un qui lit votre code, il est beaucoup plus facile de dire quelle fonction ou objet provient de quel module.

une bonne raison d'importer un module ailleurs dans le code, si elle est utilisée dans une instruction de débogage.

par exemple:

do_something_with_x(x0

je pourrais le déboguer avec:

from pprint import pprint
pprint(x)
do_something_with_x(x)

bien sûr, l'autre raison d'importer des modules ailleurs dans le code est si vous avez besoin de les importer dynamiquement. C'est parce que vous n'avez pas le choix.

Je ne me soucierais pas trop de l'efficacité du chargement du module à l'avance. Mémoire pris par le module ne sera pas très grand (en supposant qu'il soit assez modulaire) et le coût de démarrage sera négligeable.

6
répondu Jason Baker 2008-09-24 21:39:28
la source

C'est un compromis, que seul le programmeur peut décider de faire.

le cas 1 sauve un peu de mémoire et de temps de démarrage en n'important pas le module datetime (et en faisant n'importe quelle initialisation qu'il pourrait exiger) jusqu'à ce que nécessaire. Notez que faire l'import ' only when called 'signifie aussi le faire' every time when called', de sorte que chaque appel après le premier est toujours soumis aux frais supplémentaires de faire l'import.

cas 2 Sauver un peu d'exécution temps et latence en important datetime à l'avance pour que not_often_called() revienne plus rapidement quand il est appelé , et aussi en ne supportant pas la surcharge d'un import à chaque appel.

en plus de l'efficacité, il est plus facile de voir les dépendances du module à l'avance si les instructions d'importation le sont ... à l'avant. Les cacher dans le code peut rendre plus difficile de trouver facilement les modules dont quelque chose dépend.

Personnellement Je généralement suivre la PEP sauf pour des choses comme les tests unitaires et tels que je ne veux pas toujours chargé parce que je savoir ils ne vont pas être utilisés sauf pour le code de test.

6
répondu pjz 2008-09-24 21:42:05
la source

Voici un exemple où toutes les importations sont au top (c'est la seule fois où j'ai eu besoin de le faire). Je veux pouvoir terminer un sous-processus à la fois sur Un*x et Windows.

import os
# ...
try:
    kill = os.kill  # will raise AttributeError on Windows
    from signal import SIGTERM
    def terminate(process):
        kill(process.pid, SIGTERM)
except (AttributeError, ImportError):
    try:
        from win32api import TerminateProcess  # use win32api if available
        def terminate(process):
            TerminateProcess(int(process._handle), -1)
    except ImportError:
        def terminate(process):
            raise NotImplementedError  # define a dummy function

(Sur l'examen: ce Jean Millikin , a déclaré.)

6
répondu giltay 2017-05-23 15:03:09
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 trouvé que c'est un changement suffisamment utile et vous avez besoin de la vitesse supplémentaire, puis aller pour elle. Il serait probablement bon de mettre une note avec toutes les autres importations:

from foo import bar
from baz import qux
# Note: datetime is imported in SomeClass below
5
répondu Drew Stephens 2008-09-24 21:49:54
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, il est probable que vous l'importerez aussi d'autres modules de votre programme. Pour un module aussi répandu que datetime, c'est aussi probablement une dépendance pour un ensemble d'autres bibliothèques standards. La déclaration d'importation coûterait alors très peu puisque l'intialisation du module aurait déjà eu lieu. Tout ce qu'il fait à ce point est de lier l'objet du module existant à la portée locale.

coupler cette information avec l'argument pour la lisibilité et je dirais qu'il est préférable d'avoir l'énoncé d'importation à la portée du module.

4
répondu Jeremy Brown 2008-09-24 21:52:54
la source

en plus des excellentes réponses déjà données, il est intéressant de noter que l'emplacement des importations n'est pas simplement une question de style. Parfois, un module a des dépendances implicites qui doivent être importées ou initialisées en premier, et une importation de haut niveau pourrait conduire à des violations de l'ordre d'exécution requis.

ce problème apparaît souvent dans L'API Python D'Apache Spark, où vous devez initialiser SparkContext avant d'importer des paquets pyspark ou module. Il est préférable de placer les importations de pyspark dans une portée où le SparkContext est garanti d'être disponible.

4
répondu Paul 2016-04-04 17:56:03
la source

juste pour remplir réponse de Moe et la question originale:

quand nous avons affaire à des dépendances circulaires, nous pouvons faire quelques "trucs". en supposant que nous travaillons avec les modules a.py et b.py qui contiennent respectivement x() et" b y() . Puis:

  1. nous pouvons déplacer un des from imports au bas du module.
  2. Nous pouvons déplacer l'un des from imports à l'intérieur de la fonction ou de la méthode qui nécessite réellement l'Importation (Ce n'est pas toujours possible, car vous pouvez l'utiliser à partir de plusieurs endroits).
  3. Nous pouvons changer l'un des deux from imports à une importation qui ressemble à: import a

donc, pour conclure. Si vous ne faites pas face à des dépendances circulaires et ne faites pas une sorte de tour pour les éviter, alors il est préférable de mettre toutes vos importations en haut en raison des raisons déjà expliquées dans d'autres réponses à cette question. Et s'il vous plaît, en faisant ces "trucs" inclure un commentaire, il est toujours le bienvenu! :)

3
répondu Caumons 2017-05-23 14:33:26
la source

Je n'aspire pas à donner une réponse complète, parce que d'autres l'ont déjà très bien fait. Je veux juste mentionner un cas d'utilisation quand je trouve particulièrement utile pour importer des modules à l'intérieur des fonctions. Mon application utilise des paquets python et des modules stockés à certains endroits comme plugins. Pendant le démarrage de l'application, l'application parcourt tous les modules de l'emplacement et les importe, puis elle regarde à l'intérieur des modules et si elle trouve quelques points de montage pour les plugins (dans mon cas il s'agit d'une sous-classe d'une certaine classe de base ayant un ID unique) elle les enregistre. Le nombre de plugins est grande (maintenant des dizaines, mais des centaines dans le futur) et chacun d'eux est utilisé très rarement. Avoir des importations de bibliothèques tierces au sommet de mes modules de plugin était une petite pénalité lors du démarrage de l'application. En particulier certaines bibliothèques de tiers sont lourds à importer (par exemple, l'importation de plotly tente même de se connecter à internet et télécharger quelque chose qui ajoutait environ une seconde à 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 au sommet de vos modules.

3
répondu V.K. 2016-11-29 12:36:53
la source

il est intéressant que pas une seule réponse ne mentionne le traitement parallèle jusqu'à présent, où il pourrait être exigé que les importations sont dans la fonction, quand le code de fonction sérialisé est ce qui est poussé autour d'autres noyaux, par exemple comme dans le cas d'ipyparallel.

2
répondu K.-Michael Aye 2018-04-10 02:50:26
la source

j'ai été surpris de ne pas voir les coûts réels pour les vérifications de charge répétées affiché déjà, bien qu'il existe de nombreuses bonnes explications de ce à quoi s'attendre.

Si vous importez en haut, vous prenez la charge de frapper n'importe quoi. C'est assez petit, mais généralement en millisecondes, pas en nanosecondes.

si vous importez dans une fonction(s), alors vous ne prenez le hit pour le chargement si et quand une de ces fonctions est d'abord appelée. Comme beaucoup l'ont souligné, si cela n'arrive pas du tout, vous économisez le temps de chargement. Mais si la(Les) fonction (s) est (sont) appelée (s) beaucoup, vous prenez un hit répété mais beaucoup plus petit (pour vérifier qu'il a a été chargé; pas pour re-chargement). D'un autre côté, comme @aaronasterling a souligné que vous économisez également un peu parce que l'importation dans une fonction permet à la fonction d'utiliser légèrement plus rapide variable locale recherches pour identifier le nom plus tard ( /q/python-import-coding-style-69107/" %2d foo: %12.4f \xC2\xB5s" % (i, (t1-t0)*1E6)) t0 = t1 for i in xrange(5): bar() t1 = time() print(" %2d bar: %12.4f \xC2\xB5s" % (i, (t1-t0)*1E6)) t0 = t1

1
répondu TextGeek 2018-05-11 01:30:53
la source

je voudrais mentionner une de mes affaires, très similaire à celles mentionnées par @John Millikin et @V. K.:

Les Importations Facultatives

je fais des analyses de données avec le carnet Jupyter, et j'utilise le même carnet IPython comme modèle pour toutes les analyses. Dans certaines occasions, j'ai besoin d'importer Tensorflow pour faire quelques passages rapides de modèles, mais parfois je travaille dans des endroits où tensorflow n'est pas configuré / est lent à importer. Dans ce cas, je encapsuler mon Opérations Tensorflow-dépendantes dans une fonction helper, importer tensorflow à l'intérieur de cette fonction, et le lient à un bouton.

de Cette façon, je pouvais le faire "redémarrer-et-run-tout" sans avoir à attendre pour l'importation, ou d'avoir à reprendre le reste des cellules lorsqu'il échoue.

0
répondu Cedar 2018-07-05 22:37:34
la source

Autres questions sur python optimization coding-style