python subplot - Les instructions d'importation doivent-elles toujours figurer en haut d'un module?




8 Answers

L'importation de module est assez rapide, mais pas instantanée. Cela signifie que:

  • Mettre les importations en haut du module est bien, car c'est un coût trivial qui n'est payé qu'une seule fois.
  • Si vous placez les importations dans une fonction, les appels vers cette fonction prendront plus de temps.

Donc, si vous vous souciez de l'efficacité, mettez les importations au sommet. Déplacez-les seulement dans une fonction si votre profilage montre que cela aiderait (vous avez profilé pour voir où améliorer les performances, non?)

Les meilleures raisons que j'ai vu pour effectuer des importations paresseuses sont:

  • Support de bibliothèque facultatif. Si votre code a plusieurs chemins qui utilisent des bibliothèques différentes, ne cassez pas si une bibliothèque facultative n'est pas installée.
  • Dans le __init__.py d'un plugin, qui peut être importé mais pas réellement utilisé. Les exemples sont les plugins Bazaar, qui utilisent le bzrlib de chargement bzrlib de bzrlib .
title matplotlib

Le PEP 08 stipule:

Les imports sont toujours placés en haut du fichier, juste après les commentaires et les docstrings du module, et avant les globals et les constantes du module.

Cependant, si la classe / méthode / fonction que j'importe est seulement utilisée dans de rares cas, il est sûrement plus efficace de faire l'importation quand cela 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 cela?

from datetime import datetime

class SomeClass(object):

    def not_often_called(self)
        self.datetime = datetime.now()



J'ai adopté la pratique de mettre toutes les importations dans les fonctions qui les utilisent, plutôt que dans le haut du module.

Le bénéfice que j'obtiens est la capacité à refactoriser de manière plus fiable. Lorsque je déplace une fonction d'un module à un autre, je sais que la fonction continuera à fonctionner avec tout son héritage de tests intacts. Si j'ai mes importations en haut du module, quand je déplace une fonction, je trouve que je passe beaucoup de temps à faire les importations du nouveau module complet et minimal. Un IDE de refactoring peut rendre cela non pertinent.

Il y a une pénalité de vitesse comme mentionné ailleurs. J'ai mesuré cela dans ma demande et je l'ai trouvé insignifiant pour mes fins.

Il est également agréable de pouvoir voir toutes les dépendances de modules à l'avance sans recourir à la recherche (par exemple, grep). Cependant, la raison pour laquelle je m'intéresse aux dépendances de modules 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 que j'ai les dépendances au niveau du système. Je n'ai donc pas trouvé d'importations mondiales pour faciliter ma compréhension d'un système dans la pratique.

Je place généralement l'importation de sys dans la if __name__=='__main__' , puis passe les arguments (comme sys.argv[1:] ) à une fonction main() . Cela me permet d'utiliser main dans un contexte où sys n'a pas été importé.




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 et les invocations suivantes, cependant, l'approche «importer tous les appels» est en réalité moins efficace. Voir ce lien pour une technique de chargement paresseux qui combine le meilleur des deux approches en faisant une "importation paresseuse".

Mais il y a des raisons autres que l'efficacité pourquoi vous pourriez préférer l'un à l'autre. Une approche rend beaucoup plus clair à quelqu'un qui lit le code quant aux dépendances que ce module a. Ils ont également des caractéristiques de défaillance très différentes - la première échouera au moment du chargement s'il n'y a pas de module "datetime" alors que la seconde n'échouera pas avant l'appel de la méthode.

Note ajoutée: Dans IronPython, les importations peuvent être un peu plus chères que dans CPython car le code est en train d'être compilé lors de son importation.




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 importante (à supposer qu'elle 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 quel objet vient 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:

do_something_with_x(x0

Je pourrais déboguer ceci 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 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 importante (à supposer qu'elle soit suffisamment modulaire) et le coût de démarrage sera négligeable.




Voici un exemple où toutes les importations sont au sommet (c'est la seule fois que j'ai besoin de faire cela). Je veux être en mesure de terminer un sous-processus 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

(En révision: ce que John Millikin a dit.)




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 l'importerez probablement d'autres modules de votre programme. Pour un module aussi répandu que datetime, il est également probable qu'il existe une dépendance pour un grand nombre d'autres bibliothèques standard. La déclaration d'importation coûterait très peu alors puisque l'initialisation du module aurait déjà eu lieu. Tout ce qu'il fait à ce stade lie l'objet module existant à la portée locale.

Ajoutez cette information à l'argument de lisibilité et je dirais qu'il est préférable d'avoir la déclaration d'importation au niveau du module.




Juste pour compléter la réponse de Moe et la question originale:

Quand nous devons faire face à des dépendances circulaires, nous pouvons faire quelques "tours". En supposant que nous travaillons avec les modules a.py et b.py qui contiennent x() et b y() , respectivement. Alors:

  1. Nous pouvons déplacer l'une des from imports en bas du module.
  2. Nous pouvons déplacer l'une des from imports 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 à plusieurs endroits).
  3. Nous pouvons changer l'un des deux à from imports pour être une importation qui ressemble à: import a

Donc, pour conclure. Si vous ne traitez pas de dépendances circulaires et que vous ne faites pas un tour pour les éviter, il est préférable de placer toutes vos importations au sommet pour les raisons déjà expliquées dans d'autres réponses à cette question. Et s'il vous plaît, quand vous faites ce "truc" inclure un commentaire, c'est toujours le bienvenu! :)




Il est intéressant que pas une seule réponse mentionné traitement parallèle jusqu'à présent, où il pourrait être nécessaire que les importations sont dans la fonction, lorsque le code de fonction sérialisé est ce qui est poussé vers d'autres noyaux, comme dans le cas de ipyparallel.




Related

python optimization coding-style