web-dev-qa-db-fra.com

module importe et __init__.py dans Python

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?

28
Aenaon

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

Absolu

from foo import module1
from foo import module2
from foo import module3

Relative explicite

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
26
Brendan Abel

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.

4
user812786

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.

https://www.python.org/dev/peps/pep-0008/#imports

1
Eagain

Essaye ça:

package1

package1.py

__init__.py

package2

test.py

package1.py:-

class abc:
    a = 'hello'
    def print_a(self):
    print(a)

init. py: -

from .package1 import abc

package2.py:-

From package1.package1 import abc

J'utilise ces __init__.py pour importer à partir d'un package.

1
Trishant Pahwa

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.

0
brettb