J'essaie de comprendre quelles sont les meilleures pratiques en ce qui concerne la mécanique d'importation de Python (v2.7). J'ai un projet qui a commencé à grandir un peu et disons que mon code est organisé comme suit:
foo/
__init__.py
Foo.py
module1.py
module2.py
module3.py
Le nom du package est foo
et en dessous, j'ai le module Foo.py
qui contient le code de la classe Foo
. Par conséquent, j'utilise le même nom pour le package, le module et la classe, ce qui pourrait ne pas être très intelligent au départ.
__init__.py
est vide et la classe Foo
doit importer module1, module2 and module3
d'où une partie de mon Foo.py
le fichier ressemble à:
# foo/Foo.py
import module1
import module2
import module3
class Foo(object):
def __init__(self):
....
....
if __name__ == '__main__':
foo_obj = Foo()
Cependant, j'ai revu cela plus tard et j'ai pensé qu'il serait préférable d'avoir toutes les importations dans le __init__.py
fichier. D'où mon __init__.py
ressemble maintenant à:
# foo/__init__.py
import Foo
import module1
import module2
import module3
....
....
et mon Foo.py
suffit d'importer foo
:
# foo/Foo.py
import foo
Bien que cela semble pratique car il s'agit d'un seul revêtement, je suis un peu inquiet que cela puisse créer des importations circulaires. Ce que je veux dire, c'est que lorsque le script Foo.py
est exécuté, il importera tout ce qu'il peut, puis __init__.py
sera appelé pour importer Foo.py
encore (est-ce exact?). De plus, utiliser le même nom pour le package, le module et la classe rend les choses plus confuses.
Est-ce que cela a du sens comme je l'ai fait? Ou est-ce que je demande des ennuis?
Quelques choses que vous pourriez faire pour améliorer votre organisation, ne serait-ce que pour respecter certaines conventions et normes python python).
Si vous recherchez ce sujet, vous rencontrerez inévitablement des personnes recommandant les directives PEP8 . Ce sont les normes canoniques de facto pour organiser le code python.
Les modules doivent avoir des noms courts tout en minuscules. Les traits de soulignement peuvent être utilisés dans le nom du module s'il améliore la lisibilité. Python doivent également avoir des noms courts tout en minuscules, bien que l'utilisation de soulignements soit déconseillée.
Sur la base de ces directives, vos modules de projet doivent être nommés comme ceci:
foo/
__init__.py
foo.py
module1.py
module2.py
module3.py
Je trouve qu'il est généralement préférable d'éviter d'importer inutilement des modules dans __init__.py
sauf si vous le faites pour des raisons d'espace de noms. Par exemple, si vous souhaitez que l'espace de noms de votre package ressemble à ceci
from foo import Foo
au lieu de
from foo.foo import Foo
Ensuite, il est logique de mettre
from .foo import Foo
dans votre __init__.py
. À mesure que votre package s'agrandit, certains utilisateurs peuvent ne pas vouloir utiliser tous les sous-packages et modules, il n'est donc pas logique de forcer l'utilisateur à attendre que tous ces modules se chargent en les important implicitement dans votre __init__.py
. De plus, vous devez vous demander si vous voulez même module1
, module2
, et module3
dans le cadre de votre API externe. Sont-ils uniquement utilisés par Foo
et non destinés aux utilisateurs finaux? S'ils ne sont utilisés qu'en interne, ne les incluez pas dans le __init__.py
Je recommanderais également d'utiliser importations relatives absolues ou explicites pour importer des sous-modules. Par exemple, dans foo.py
from foo import module1
from foo import module2
from foo import module3
from . import module1
from . import module2
from . import module3
Cela évitera tout problème de dénomination possible avec d'autres packages et modules. Cela vous facilitera également la tâche si vous décidez de prendre en charge Python3, car la syntaxe d'importation implicite relative que vous utilisez actuellement n'est pas prise en charge dans Python3.
De plus, les fichiers à l'intérieur de votre package ne doivent généralement pas contenir
if __name__ == '__main__'
En effet, l'exécution d'un fichier en tant que script signifie qu'il ne sera pas considéré comme faisant partie du package auquel il appartient, il ne pourra donc pas effectuer d'importations relatives.
La meilleure façon de fournir des scripts exécutables aux utilisateurs consiste à utiliser scripts
ou console_scripts
fonction de setuptools
. La façon dont vous organisez vos scripts peut être différente selon la méthode que vous utilisez, mais j'organise généralement le mien comme ceci:
foo/
__init__.py
foo.py
...
scripts/
foo_script.py
setup.py
Selon PEP 0008, "Interfaces publiques et internes" :
Les noms importés doivent toujours être considérés comme un détail d'implémentation. Les autres modules ne doivent pas s'appuyer sur un accès indirect à ces noms importés, sauf s'ils font partie explicitement documentée de l'API du module conteneur, comme os.path ou le module
__init__
D'un package qui expose les fonctionnalités des sous-modules.
Cela suggérerait donc qu'il est ok de placer les importations dans le module __init__
, Si __init__
Est utilisé pour exposer des fonctions de sous-modules. Ici est un court article de blog que j'ai trouvé avec quelques exemples d'utilisations Pythonic de __init__
, En utilisant les importations pour rendre les sous-packages disponibles au niveau du package.
Votre exemple de déplacement des instructions d'importation vers __init__
Afin de n'avoir qu'une seule importation dans Foo
, ne semble pas suivre cette règle. Mon interprétation est que les importations dans votre __init__
Devraient être utilisées pour les interfaces externe, sinon, mettez simplement vos instructions d'importation dans le fichier qui en a besoin. Cela vous évite des problèmes lorsque les noms des sous-modules changent et vous évite les importations inutiles ou difficiles à trouver lorsque vous ajoutez d'autres fichiers qui utilisent un sous-ensemble différent de sous-modules.
En ce qui concerne les références circulaires, cela est certainement possible dans Python ( par exemple ). J'ai écrit à ce sujet avant d'essayer votre exemple de jouet, mais pour faire l'exemple travail, je devais monter Foo.py
à un niveau, comme ceci:
Foo.py
foo/
__init__.py
module1.py
module2.py
module3.py
Avec cette configuration et certaines instructions d'impression, l'exécution de python Foo.py
Donne la sortie:
module 1
module 2
module 3
hello Foo constructor
et sort normalement. Notez que cela est dû à l'ajout du if __name__ == "__main__"
- si vous ajoutez une instruction d'impression en dehors de cela, vous pouvez voir Python charge toujours le module deux fois. Une meilleure solution serait pour supprimer l'importation de votre __init__.py
. Comme je l'ai dit plus tôt, cela peut ou non avoir du sens, en fonction de ces sous-modules.
Vous pouvez vous référer au 'Guide de style pour Python Code' pour les meilleures pratiques, l'importation est conservée dans la classe de ce guide.
Essaye ça:
package1.py
__init__.py
test.py
class abc:
a = 'hello'
def print_a(self):
print(a)
from .package1 import abc
From package1.package1 import abc
J'utilise ces __init__.py pour importer à partir d'un package.
Je ne peux pas dire définitivement si c'est la bonne façon, mais je l'ai toujours fait à l'ancienne. Autrement dit, j'ai toujours gardé __init__.py
vide, et vient d'importer des choses dans Foo.py
comme requis.
D'après la façon dont vous le décrivez, il semble qu'il y ait une logique circulaire sous cette dernière forme.