537 votes

Les déclarations d'importation doivent-elles toujours figurer au début d'un module ?

PEP 8 États :

Les importations sont toujours placées en haut du fichier, juste après les commentaires et les docstrings du 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 d'effectuer l'importation lorsqu'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()

382voto

John Millikin Points 86775

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

  • Mettre les importations en haut du module est bien, parce que c'est un coût trivial qui n'est payé qu'une fois.
  • En plaçant les importations dans une fonction, les appels à cette fonction seront plus longs.

Donc, si vous vous souciez de l'efficacité, mettez les importations en haut. Ne les déplacez dans une fonction que si votre profilage montre que cela serait utile (vous a fait pour voir où améliorer les performances, non ? ?)


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

  • Prise en charge des bibliothèques facultatives. Si votre code comporte plusieurs chemins d'accès qui utilisent des bibliothèques différentes, il n'y a pas de rupture si une bibliothèque optionnelle 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 bzrlib Le cadre de chargement paresseux de la Commission européenne.

26 votes

Cette réponse se concentre sur le import le coût de l'état des lieux, mais pas le coût de ce que est en cours d'importation. Prenez en considération la bibliothèque importée. Certaines bibliothèques peuvent exécuter des opérations coûteuses (en temps d'exécution ou en coûts de mémoire) lors de l'importation.

0 votes

Dennis, c'est parce que le coût initial de l'importation est constant, quel que soit le moment où vous le faites. Le processus de chargement auquel vous faites référence n'a pas lieu une seconde fois. La réponse fait référence à la vérification qui doit être effectuée dans sys.modules lorsqu'une instruction d'importation est rencontrée.

16 votes

Je ne suis pas d'accord. Parfois, vous vous souciez de ce coût que vous " payez une fois ", car vous le payez à chaque fois vous démarrez votre programme, et avant votre code commence à faire quelque chose d'utile, et certains modules communs prennent un temps considérable pour être importés. Prenez numpy, pyplot, cv2, pandas, chaque de ces modules prennent un temps compris entre 0,2 et 1 seconde pour être importés sur mon PC. Cela rend le démarrage assez lent. D'un autre côté, une importation à l'intérieur d'une fonction passera inaperçue à moins que vous ne la mettiez dans un code critique que vous ne devriez pas coder en python. Réimporter le même module 1000000 fois prend environ 0.1s.

106voto

Moe Points 6698

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 s'importer l'un l'autre, cela provoquera une dépendance circulaire lorsque vous importerez l'un des modules, provoquant une boucle infinie. Si vous déplacez l'instruction d'importation dans l'un des modules, il n'essaiera pas d'importer l'autre module avant que la fonction ne 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

3 votes

Oui mais on peut entrer dans l'enfer de la dépendance.

59 votes

Si deux modules doivent s'importer l'un l'autre, quelque chose ne va pas du tout dans le code.

5 votes

La programmation orientée objet me conduit souvent à des dépendances circulaires. Une classe d'objet essentielle peut être importée dans plusieurs modules. Pour que cet objet puisse effectuer ses propres tâches, il peut avoir besoin de faire appel à un ou plusieurs de ces modules. Il existe des moyens d'éviter cela, comme envoyer des données à l'objet par le biais d'arcs de fonction, pour lui permettre d'accéder à l'autre module. Mais il y a des moments où faire cela semble très contre-intuitif à la POO (le monde extérieur ne devrait pas avoir besoin de savoir comment il accomplit la tâche dans cette fonction).

84voto

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

Le bénéfice que j'en retire est la possibilité de refactorer 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 intact. Si mes importations se trouvent en haut du module, lorsque je déplace une fonction, je passe beaucoup de temps à faire en sorte que les importations du nouveau module soient complètes et minimales. Un IDE de refactoring pourrait rendre cela inutile.

Il y a une pénalité de vitesse comme mentionné ailleurs. Je l'ai mesurée dans mon application et je l'ai trouvée insignifiante pour mes besoins.

Il est également agréable de pouvoir voir toutes les dépendances des modules sans avoir à recourir à la recherche (par exemple, grep). Cependant, la raison pour laquelle je m'intéresse aux dépendances des modules est généralement parce que j'installe, remanie ou déplace un système entier comprenant plusieurs fichiers, et pas seulement un seul module. Dans ce cas, je vais de toute façon effectuer une recherche globale pour m'assurer que je dispose des dépendances au niveau du système. Je n'ai donc pas trouvé que les importations globales m'aident à comprendre un système dans la pratique.

Je mets habituellement l'importation de sys à l'intérieur de la if __name__=='__main__' et ensuite passer des arguments (comme sys.argv[1:] ) à un main() fonction. Cela me permet d'utiliser main dans un contexte où sys n'a pas été importé.

46voto

La plupart du temps, ce serait utile pour la clarté et judicieux de le faire, mais ce n'est pas toujours le cas. Vous trouverez ci-dessous quelques exemples de circonstances dans lesquelles les importations de modules peuvent être placées ailleurs.

Tout d'abord, vous pourriez avoir un module avec un test unitaire du formulaire :

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

Deuxièmement, vous pouvez avoir besoin d'importer de manière conditionnelle un module différent au moment de l'exécution.

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

Il existe probablement d'autres situations où vous pouvez placer des importations dans d'autres parties du code.

19voto

Curt Hagenlocher Points 12432

La première variante est en effet plus efficace que la seconde lorsque la fonction est appelée zéro ou une fois. En revanche, à partir de la deuxième invocation, 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 un "import paresseux".

Mais il existe des raisons autres que l'efficacité pour lesquelles vous pourriez préférer l'un à l'autre. L'une de ces approches permet à une personne lisant le code de savoir plus clairement quelles sont les dépendances de ce module. Ils ont également des caractéristiques d'échec 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 avant que la méthode ne soit appelée.

Note ajoutée : Dans IronPython, les importations peuvent être un peu plus coûteuses que dans CPython parce que le code est essentiellement compilé pendant qu'il est importé.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X