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()

15voto

Patrick Points 523

Voici une mise à jour résumé des réponses à cette y connexe questions.

  • PEP 8 recommande en mettant les importations en tête de liste.
  • C'est souvent plus pratique pour obtenir ImportError lorsque vous exécutez votre programme pour la première fois plutôt que lorsque votre programme appelle votre fonction pour la première fois.
  • Les importations au sommet améliorent lisibilité , puisque vous pouvez voir tous vos Dépendances en un coup d'œil.
  • Refactoring pourrait être plus facile si les importations sont situées dans la fonction où ils sont utilisés (ce qui facilite son déplacement vers un autre module). On peut également faire valoir que cela est bon pour lisibilité .
  • Placer les importations dans la portée de la fonction peut aider à éviter les problèmes avec importations circulaires .
  • Placer les importations dans la portée de la fonction permet de garder le module l'espace-nom , afin qu'il n'apparaisse pas parmi les suggestions de complétion de tabulation.
  • Temps de démarrage : les importations dans une fonction ne s'exécuteront pas avant (si) cette fonction est appelée. Cela peut être important avec des bibliothèques lourdes.
  • Même si les déclarations d'importation sont super rapides lors des exécutions suivantes, elles subissent toujours un pénalité de vitesse ce qui peut être significatif si la fonction est triviale mais fréquemment utilisée.
  • Importations dans le cadre du __name__ == "__main__" garde semble très raisonnable .
  • Il ne semble pas clair si dynamique ou conditionnel les importations privilégient un style plutôt qu'un autre.

9voto

Dan Points 18831

Curt soulève 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 (a) il est assez rapide, et (b) il n'a lieu qu'au démarrage.

Si vous devez charger des modules lourds à des moments inattendus, il est probablement plus judicieux de les charger dynamiquement à l'aide de la commande __import__ fonction, et être bien sûr pour attraper ImportError les exceptions, et les traiter de manière raisonnable.

8voto

Jason Baker Points 56682

Je ne m'inquiéterais pas trop de l'efficacité du chargement du module au départ. La mémoire occupée par le module ne sera pas très grande (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 ainsi 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 qu'il est utilisé dans une déclaration de débogage.

Par exemple :

do_something_with_x(x)

Je pourrais déboguer ça 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 que vous devez les importer dynamiquement. En effet, vous n'avez pratiquement pas le choix.

Je ne m'inquiéterais pas trop de l'efficacité du chargement du module en amont. La mémoire occupée par le module ne sera pas très grande (en supposant qu'il soit suffisamment modulaire) et le coût de démarrage sera négligeable.

8voto

TextGeek Points 143

J'ai été surpris de ne pas voir les coûts réels des contrôles répétés de la charge déjà affichés, bien qu'il y ait beaucoup de bonnes explications sur ce à quoi il faut s'attendre.

Si vous importez au sommet, vous prenez le coup de charge quoi qu'il arrive. C'est assez faible, mais généralement en millisecondes, pas en nanosecondes.

Si vous importez à l'intérieur d'une (ou plusieurs) fonction(s), vous ne prenez que le coup pour le chargement. si y quand l'une de ces fonctions est appelée en premier. Comme beaucoup l'ont souligné, si cela ne se produit pas du tout, vous économisez le temps de chargement. Mais si la (les) fonction(s) est (sont) appelée(s) souvent, vous prenez un coup répété mais beaucoup plus petit (pour vérifier qu'elle(s) a a été chargé ; pas pour le rechargement proprement dit). D'un autre côté, comme @aaronasterling l'a souligné, vous économisez aussi un peu parce que l'importation dans une fonction permet à la fonction d'utiliser des données légèrement plus rapides. variable locale 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 à l'intérieur d'une fonction. Les temps rapportés (en Python 2.7.14 sur un Intel Core i7 à 2,3 GHz) sont indiqués ci-dessous (le fait que le 2e appel prenne plus de temps que les appels suivants semble cohérent, bien que je ne sache pas pourquoi).

 0 foo:   14429.0924 µs
 1 foo:      63.8962 µs
 2 foo:      10.0136 µs
 3 foo:       7.1526 µs
 4 foo:       7.8678 µs
 0 bar:       9.0599 µs
 1 bar:       6.9141 µs
 2 bar:       7.1526 µs
 3 bar:       7.8678 µs
 4 bar:       7.1526 µs

Le code :

from __future__ import print_function
from time import time

def foo():
    import collections
    import re
    import string
    import math
    import subprocess
    return

def bar():
    import collections
    import re
    import string
    import math
    import subprocess
    return

t0 = time()
for i in xrange(5):
    foo()
    t1 = time()
    print("    %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

6voto

pjz Points 11925

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

Le cas 1 permet d'économiser de la mémoire et du temps de démarrage en n'important le module datetime (et en effectuant toute initialisation qu'il pourrait nécessiter) qu'en cas de besoin. Notez que faire l'importation "seulement quand on l'appelle" signifie aussi la faire "à chaque fois qu'on l'appelle", donc chaque appel après le premier subit toujours la surcharge supplémentaire de l'importation.

Cas 2 : économisez un peu de temps d'exécution et de latence en important datetime à l'avance afin que not_often_called() retourne plus rapidement lorsqu'elle est appelé, et aussi en ne subissant pas les frais généraux d'une importation à chaque appel.

En plus de l'efficacité, il est plus facile de voir les dépendances des modules en amont si les déclarations d'importation sont ... en amont. En les cachant dans le code, il est plus difficile de trouver facilement de quels modules quelque chose dépend.

Personnellement, je suis généralement la PEP, sauf pour des choses comme les tests unitaires et autres, que je ne veux pas toujours charger parce que je connaître ils ne seront pas utilisés, sauf pour le code de test.

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