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


Answers

Mettre l'instruction import dans une fonction peut empêcher les dépendances circulaires. Par exemple, si vous avez deux modules, X.py et Y.py, et qu'ils doivent tous deux s'importer, 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 plus d'informations - effbot.org/zone/import-confusion.htm

Question

Le PEP 08 stipule:

Les imports sont toujours placés en haut du fichier, juste après les commentaires et 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()



La plupart du temps, cela serait utile par souci de clarté et de bon sens, mais ce n'est pas toujours le cas. Vous trouverez ci-dessous quelques exemples de circonstances dans lesquelles les importations de modules peuvent se trouver ailleurs.

Premièrement, 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 pourriez avoir besoin d'importer conditionnellement un module différent à l'exécution.

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

Il y a probablement d'autres situations où vous pourriez placer des importations dans d'autres parties du code.




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 façon inattendue.

Normalement, je ne m'inquiète pas de l'efficacité du chargement des modules, car (a) est 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 sensé de les charger dynamiquement avec la fonction __import__ , et assurez-vous d'attraper les exceptions ImportError , et de les gérer de manière raisonnable.




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




C'est comme beaucoup d'autres optimisations - vous sacrifiez une certaine lisibilité pour la vitesse. Comme l'a mentionné John, si vous avez fait vos devoirs de profilage et que vous avez trouvé que c'est un changement assez important 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:

from foo import bar
from baz import qux
# Note: datetime is imported in SomeClass below



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! :)




Je n'aspire pas à fournir une réponse complète, car d'autres l'ont déjà fait très bien. Je veux juste mentionner un cas d'utilisation quand je trouve particulièrement utile d'importer des modules dans les fonctions. Mon application utilise des paquets python et des modules stockés à certains endroits en tant que plugins. Au démarrage de l'application, l'application parcourt tous les modules de l'emplacement et les importe, puis regarde à l'intérieur des modules et trouve des points de montage pour les plugins (dans mon cas c'est une sous-classe d'une classe de base ayant un ID) il les enregistre. Le nombre de plugins est grand (maintenant des dizaines, mais peut-être des centaines dans le futur) et chacun d'eux est utilisé assez rarement. Avoir des importations de bibliothèques tierces en haut de mes modules plugin était un peu pénalisé lors du démarrage de l'application. En particulier, certaines bibliothèques tierces sont lourdes à importer (par exemple, l'importation d'intrigues tente même de se connecter à Internet et de télécharger quelque chose qui ajoutait environ une seconde au démarrage). En optimisant les imports (en ne les appelant que dans les fonctions où ils sont utilisés) dans les plugins, j'ai réussi à réduire le démarrage de 10 secondes à 2 secondes. C'est une grosse différence pour mes utilisateurs.

Donc, ma réponse est non, ne mettez pas toujours les importations au sommet de vos modules.