web-dev-qa-db-fra.com

Pourquoi ai-je besoin de __init__.py à tous les niveaux?

Étant donné que j'ai la structure de répertoires suivante avec . étant le répertoire de travail actuel

.
\---foo
    \---bar
        \---__init__.py
        \---baz.py

Quand je lance python -c "import foo.bar.baz" Je reçois

Traceback (most recent call last):
  File "<string>", line 1
ImportError: No module named foo.bar.baz

Si je echo "" > foo/__init__.py, la commande ci-dessus fonctionne.

Suis-je en train de faire quelque chose de mal ou ai-je mal compris le point de __init__.py? Je pensais que c'était pour empêcher les modules d'exister là où ils ne devraient pas, par exemple un répertoire nommé string, mais si vous remplacez foo par string dans mon exemple, je suis apparemment obligé de créer le module qui ne devrait jamais être utilisé, juste pour que je puisse référencer un fichier plus profondément dans la hiérarchie.

Mise à jour

Je travaille avec un système de génération qui génère le __init__.py est pour moi et applique la structure du répertoire et bien que je puisse jouer avec la hiérarchie, je préfère simplement ajouter le __init__.py moi même. Pour changer légèrement la question, pourquoi ai-je besoin d'un package python à tous les niveaux au lieu de simplement en haut? Est-ce juste une règle selon laquelle vous ne pouvez importer que des modules à partir du python ou à partir d'une chaîne de paquets hors du chemin python?

13
quittle

Oui, ce fichier est requis si vous souhaitez que le répertoire soit traité comme module.

Les fichiers __init__.py Sont nécessaires pour que Python traite les répertoires comme contenant des packages; cela est fait pour empêcher les répertoires avec un nom commun, tel que chaîne, de masquer involontairement des modules valides qui se produire plus tard sur le chemin de recherche du module. Dans le cas le plus simple, __init__.py peut simplement être un fichier vide, mais il peut également exécuter le code d'initialisation du package ou définir la variable __all__, décrite plus loin.

https://docs.python.org/3/tutorial/modules.html#packages

J'essaie de créer un __init__.py Non vide. Vous avez une grande possibilité de documenter le module, de vous débarrasser des importations imbriquées pour les utilisateurs/développeurs en fournissant les objets les plus utiles (classes/fonctions) au premier niveau ... ... pour être aussi simple à utiliser que possible dans contrairement à - disons - le Java importe

Modifier après la mise à jour de la question

Les importateurs/chercheurs par défaut (examinez le sys.meta_path) Sont:

  1. BuiltinImporter - recherche/charge les modules intégrés
  2. FrozenImporter - recherche/charge les modules figés (par exemple * .pyc)
  3. PathFinder - celui qui vous intéresse, permet de rechercher/charger des modules en fonction du système de fichiers

Le troisième est la chose __init__.py (En fait FrozenImporter aussi).

PathFinder recherche le module dans les chemins de sys.path (Et dans __path__ Défini dans les packages). Le module peut être autonome python (s'il est à la racine du chemin recherché) ou répertoire avec __init__.py.

En vous référant à votre exemple:

foo/
  bar/
    __init__.py
    baz.py
  • Si vous créez _init__.py Dans foo/, foo.bar.baz Sera disponible (comme vous l'avez dit).

  • Si vous ajoutez foo/ À sys.path Ou le passez par PYTHONPATH=foo/, bar.baz Sera disponible (remarque sans module parent foo).

  • Si vous écrivez votre propre Finder (et Loader) vous pouvez par exemple charger n'importe quel fichier que vous voulez malgré où il se trouve. Cela vous donne un grand pouvoir. Par exemple, jetez un œil à stack-overflow-import , expose le code basé sur les résultats de recherche de SO.

8
kwarunek