web-dev-qa-db-fra.com

Est-ce Pythonic d'importer des fonctions internes?

PEP 8 dit:

  • Les importations sont toujours placées en haut du fichier, juste après les commentaires et les docstrings de module, et avant les globales et les constantes du module.

À l'occasion, je viole PEP 8. Parfois, j'importe des choses à l'intérieur des fonctions. En règle générale, je fais cela s'il y a une importation qui n'est utilisée que dans une seule fonction.

Des opinions?

EDIT (la raison pour laquelle je pense que l'importation dans les fonctions peut être une bonne idée):

Raison principale: cela peut rendre le code plus clair.

  • Lorsque je regarde le code d'une fonction, je peux me demander: "Qu'est-ce que la fonction/classe xxx?" (xxx étant utilisé dans la fonction). Si j'ai toutes mes importations en haut du module, je dois y aller pour déterminer ce qu'est xxx. Il s'agit plus d'un problème lors de l'utilisation de from m import xxx. Voyant m.xxx dans la fonction m'en dit probablement plus. Selon ce qu'est m: s'agit-il d'un module/package de premier niveau bien connu (import m)? Ou s'agit-il d'un sous-module/package (from a.b.c import m)?
  • Dans certains cas, le fait d'avoir ces informations supplémentaires ("Qu'est-ce que xxx?") Près de l'endroit où xxx est utilisé peut rendre la fonction plus facile à comprendre.
107
codeape

À long terme, je pense que vous apprécierez d'avoir la plupart de vos importations en haut du fichier, de cette façon, vous pouvez dire en un coup d'œil à quel point votre module est compliqué par ce qu'il doit importer.

Si j'ajoute du nouveau code à un fichier existant, je ferai généralement l'importation là où c'est nécessaire, puis si le code reste, je rendrai les choses plus permanentes en déplaçant la ligne d'importation en haut du fichier.

Un autre point, je préfère obtenir une exception ImportError avant l'exécution de tout code - comme vérification de cohérence, c'est donc une autre raison d'importer en haut.

J'utilise pyChecker pour vérifier les modules inutilisés.

73
Peter Ericson

Il y a deux fois où je viole PEP 8 à cet égard:

  • Importations circulaires: le module A importe le module B, mais quelque chose dans le module B a besoin du module A (bien que ce soit souvent un signe que je dois refactoriser les modules pour éliminer la dépendance circulaire)
  • Insertion d'un point d'arrêt pdb: import pdb; pdb.set_trace() C'est pratique b/c je ne veux pas mettre import pdb en haut de chaque module, je voudrais peut-être déboguer, et il est facile de se rappeler de supprimer l'importation lorsque je supprime le point d'arrêt.

En dehors de ces deux cas, c'est une bonne idée de tout mettre en haut. Cela rend les dépendances plus claires.

39
Rick Copeland

Voici les quatre cas d'utilisation d'importation que nous utilisons

  1. import (et from x import y et import x as y) au sommet

  2. Choix pour l'importation. Au sommet.

    import settings
    if setting.something:
        import this as foo
    else:
        import that as foo
    
  3. Importation conditionnelle. Utilisé avec JSON, bibliothèques XML et similaires. Au sommet.

    try:
        import this as foo
    except ImportError:
        import that as foo
    
  4. Importation dynamique. Jusqu'à présent, nous n'en avons qu'un exemple.

    import settings
    module_stuff = {}
    module= __import__( settings.some_module, module_stuff )
    x = module_stuff['x']
    

    Notez que cette importation dynamique n'apporte pas de code, mais apporte des structures de données complexes écrites en Python. C'est un peu comme une donnée marinée, sauf que nous l'avons marinée à la main.

    C'est aussi, plus ou moins, en haut d'un module


Voici ce que nous faisons pour rendre le code plus clair:

  • Gardez les modules courts.

  • Si j'ai toutes mes importations en haut du module, je dois y aller pour déterminer ce qu'est un nom. Si le module est court, c'est facile à faire.

  • Dans certains cas, avoir ces informations supplémentaires à proximité de l'endroit où un nom est utilisé peut rendre la fonction plus facile à comprendre. Si le module est court, c'est facile à faire.

19
S.Lott

Une chose à garder à l'esprit: les importations inutiles peuvent entraîner des problèmes de performances. Donc, si c'est une fonction qui sera appelée fréquemment, vous feriez mieux de placer l'importation en haut. Bien sûr, cela est une optimisation, donc s'il y a un cas valide à faire que l'importation à l'intérieur d'une fonction est plus clair que l'importation en haut d'un fichier, cela surpasse les performances dans la plupart des cas.

Si vous faites IronPython, on me dit qu'il est préférable d'importer des fonctions internes (car la compilation de code dans IronPython peut être lente). Ainsi, vous pourrez alors obtenir un moyen d'importer des fonctions internes. Mais à part cela, je dirais que cela ne vaut tout simplement pas la peine de lutter contre les conventions.

En règle générale, je fais cela s'il y a une importation qui n'est utilisée que dans une seule fonction.

Un autre point que j'aimerais souligner est que cela peut être un problème de maintenance potentiel. Que se passe-t-il si vous ajoutez une fonction qui utilise un module qui était auparavant utilisé par une seule fonction? Allez-vous vous rappeler d'ajouter l'importation en haut du fichier? Ou allez-vous analyser chaque fonction pour les importations?

FWIW, il y a des cas où il est logique d'importer à l'intérieur d'une fonction. Par exemple, si vous souhaitez définir la langue dans cx_Oracle, vous devez définir une variable d'environnement NLS _ LANG avant elle est importée. Ainsi, vous pouvez voir du code comme celui-ci:

import os

Oracle = None

def InitializeOracle(lang):
    global Oracle
    os.environ['NLS_LANG'] = lang
    import cx_Oracle
    Oracle = cx_Oracle
7
Jason Baker

J'ai déjà enfreint cette règle pour les modules qui sont auto-testés. Autrement dit, ils ne sont normalement utilisés que pour le support, mais je leur définis un principal de sorte que si vous les exécutez seuls, vous pouvez tester leurs fonctionnalités. Dans ce cas, j'importe parfois getopt et cmd juste en principal, car je veux qu'il soit clair pour quelqu'un qui lit le code que ces modules n'ont rien à voir avec le fonctionnement normal du module et ne sont inclus que pour les tests.

6
Daniel Lew

Venant de la question sur chargement du module deux fois - Pourquoi pas les deux?

Une importation en haut du script indiquera les dépendances et une autre importation dans la fonction avec rendra cette fonction plus atomique, tout en ne causant apparemment aucun inconvénient de performances, car une importation consécutive est bon marché.

4
IljaBek

Tant que c'est import et non from x import *, vous devez les mettre en haut. Il ajoute un seul nom à l'espace de noms global et vous vous en tenez à PEP 8. De plus, si vous en avez besoin plus tard ailleurs, vous n'avez rien à déplacer.

Ce n'est pas grave, mais comme il n'y a presque aucune différence, je suggère de faire ce que dit PEP 8.

2
Javier

Jetez un œil à l'approche alternative utilisée dans sqlalchemy: injection de dépendance:

@util.dependencies("sqlalchemy.orm.query")
def merge_result(query, *args):
    #...
    query.Query(...)

Remarquez comment la bibliothèque importée est déclarée dans un décorateur et passée comme argument à la fonction!

Cette approche rend le code plus propre et fonctionne également 4,5 fois plus rapidement qu'une instruction import!

Référence: https://Gist.github.com/kolypto/589e84fbcfb6312532658df2fabdb796

2
kolypto