À quoi sert __init__.py?


Réponses:


1456

Il faisait partie intégrante d'un package ( ancien, "package normal" antérieur à 3.3 , et non plus récent "package d'espace de noms" 3.3+ ).

Voici la documentation.

Python définit deux types de packages, les packages standard et les packages d'espace de noms. Les packages réguliers sont des packages traditionnels tels qu'ils existaient dans Python 3.2 et versions antérieures. Un package standard est généralement implémenté comme un répertoire contenant un __init__.pyfichier. Lorsqu'un package standard est importé, ce __init__.pyfichier est implicitement exécuté et les objets qu'il définit sont liés à des noms dans l'espace de noms du package. Le __init__.pyfichier peut contenir le même code Python que tout autre module peut contenir, et Python ajoutera des attributs supplémentaires au module lors de son importation.

Mais cliquez simplement sur le lien, il contient un exemple, plus d'informations et une explication des packages d'espace de noms, le type de packages sans __init__.py.


187
Qu'est-ce que cela signifie: "ceci est fait pour empêcher les répertoires avec un nom commun, comme une chaîne, de cacher involontairement des modules valides qui se produisent plus tard sur le chemin de recherche du module"?
Carl G

97
@CarlG Python recherche une liste de répertoires pour résoudre les noms, par exemple, les instructions d'importation. Étant donné que ceux-ci peuvent être n'importe quel répertoire et que des utilisateurs arbitraires peuvent être ajoutés par l'utilisateur final, les développeurs doivent se soucier des répertoires qui partagent un nom avec un module Python valide, tels que 'string' dans l'exemple de documentation. Pour y remédier, il ignore les répertoires qui ne contiennent pas de fichier nommé _ _ init _ _.py (pas d'espaces), même s'il est vide.
Two-Bit Alchemist

186
@CarlG Essayez ceci. Créez un répertoire appelé «datetime» et créez deux fichiers vides, le fichier init.py (avec des traits de soulignement) et datetime.py. Ouvrez maintenant un interpréteur, importez sys et lancez sys.path.insert(0, '/path/to/datetime'), en remplaçant ce chemin par le chemin vers le répertoire que vous venez de créer. Maintenant, essayez quelque chose comme from datetime import datetime;datetime.now(). Vous devriez obtenir une AttributeError (car il importe maintenant votre fichier vierge). Si vous deviez répéter ces étapes sans créer le fichier init vide, cela ne se produirait pas. C'est ce que l'on veut empêcher.
Two-Bit Alchemist

4
@ DarekNędza Vous avez quelque chose de mal configuré si vous ne pouvez pas simplement ouvrir un interpréteur Python et émettre from datetime import datetimesans erreur. C'est bien depuis la version 2.3!
Two-Bit Alchemist

5
@SWang: C'est incorrect: builtinsliste les fonctions et classes intégrées, pas les modules intégrés (cf. docs.python.org/3/tutorial/modules.html#the-dir-function ). Si vous voulez lister les modules intégrés , faites-le import sys; print(sys.builtin_module_names)(cf. docs.python.org/3/library/sys.html#sys.builtin_module_names ).
Maggyero

846

Les fichiers nommés __init__.pysont utilisés pour marquer les répertoires sur le disque en tant que répertoires de packages Python. Si vous avez les fichiers

mydir/spam/__init__.py
mydir/spam/module.py

et mydirest sur votre chemin, vous pouvez importer le code en module.pytant que

import spam.module

ou

from spam import module

Si vous supprimez le __init__.pyfichier, Python ne recherchera plus de sous-modules dans ce répertoire, donc les tentatives d'importation du module échoueront.

Le __init__.pyfichier est généralement vide, mais peut être utilisé pour exporter des parties sélectionnées du package sous un nom plus pratique, conserver des fonctions pratiques, etc. Étant donné l'exemple ci-dessus, le contenu du module init est accessible comme

import spam

basé sur cela


96
Mise à jour: le fichier __init__.pyétait requis sous Python 2.X et est toujours requis sous Python 2.7.12 (je l'ai testé) mais il n'est plus requis à partir de (prétendument) Python 3.3 et n'est plus requis sous Python 3.4.3 (I testé). Voir stackoverflow.com/questions/37139786 pour plus de détails.
Rob_before_edits

4
Ne l'utilisez pas. C'est un package "namespace", pas un package normal. Le package namespace est utilisé pour de très rares cas d'utilisation. Vous n'aurez peut-être pas besoin de savoir quand l'utiliser. Utilisez simplement __init__.py.
méthane

2
cependant si vous en avez setup.pyet que vous l'utilisez find_packages()il faut en avoir __init__.pydans chaque répertoire. Voir stackoverflow.com/a/56277323/7127824
techkuz

484

En plus d'étiqueter un répertoire en tant que package Python et de le définir __all__, __init__.pyvous permet de définir n'importe quelle variable au niveau du package. Cela est souvent pratique si un package définit quelque chose qui sera importé fréquemment, à la manière d'une API. Ce modèle favorise l'adhésion à la philosophie Pythonique "plat est meilleur que imbriqué".

Un exemple

Voici un exemple de l'un de mes projets, dans lequel j'importe fréquemment un sessionmakerappelé Sessionpour interagir avec ma base de données. J'ai écrit un package "base de données" avec quelques modules:

database/
    __init__.py
    schema.py
    insertions.py
    queries.py

Mon __init__.pycontient le code suivant:

import os

from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine

engine = create_engine(os.environ['DATABASE_URL'])
Session = sessionmaker(bind=engine)

Puisque je définis Sessionici, je peux démarrer une nouvelle session en utilisant la syntaxe ci-dessous. Ce code serait le même exécuté de l'intérieur ou de l'extérieur du répertoire du package "base de données".

from database import Session
session = Session()

Bien sûr, c'est une petite commodité - l'alternative serait de définir Sessiondans un nouveau fichier comme "create_session.py" dans mon package de base de données, et de démarrer de nouvelles sessions en utilisant:

from database.create_session import Session
session = Session()

Lectures complémentaires

Il y a un fil reddit assez intéressant couvrant les utilisations appropriées d' __init__.pyici:

http://www.reddit.com/r/Python/comments/1bbbwk/whats_your_opinion_on_what_to_include_in_init_py/

L'opinion majoritaire semble être que les __init__.pyfichiers doivent être très fins pour éviter de violer la philosophie "explicite vaut mieux qu'implicite".


3
engine, sessionmaker, create_engineEt ospeut tout aussi être importés d' databaseaujourd'hui ... semble que vous avez fait un gâchis de cet espace de noms.
ArtOfWarfare

9
@ArtOfWarfare, vous pouvez utiliser __all__ = [...]pour limiter ce qui est importé avec import *. Mais à part cela, oui, vous vous retrouvez avec un espace de noms de premier niveau en désordre.
Nathan Gould

Puis-je savoir ce qu'est l'URL DE LA BASE DE DONNÉES? J'ai essayé de reproduire cela en enfermant le create_engine avec 'mysql + mysqldb: // root: python @ localhost: 3306 / test' mais cela ne fonctionne pas. Merci.
SunnyBoiz

2
Comment accéderiez- vous à la classe 'Session' définie dans init de l'intérieur du paquet, par exemple quieries.py?
vldbnc

253

Il y a 2 raisons principales pour __init__.py

  1. Pour plus de commodité: les autres utilisateurs n'auront pas besoin de connaître l'emplacement exact de vos fonctions dans la hiérarchie de vos packages.

    your_package/
      __init__.py
      file1.py
      file2.py
        ...
      fileN.py
    # in __init__.py
    from file1 import *
    from file2 import *
    ...
    from fileN import *
    # in file1.py
    def add():
        pass

    alors d'autres peuvent appeler add () par

    from your_package import add

    sans connaître file1, comme

    from your_package.file1 import add
  2. Si vous voulez que quelque chose soit initialisé; par exemple, la journalisation (qui devrait être placée au niveau supérieur):

    import logging.config
    logging.config.dictConfig(Your_logging_config)

7
oh, avant de lire votre réponse, je pensais que l'appel explicite d'une fonction à partir de son emplacement était une bonne pratique.
Aerin

2
@Aerin, il serait préférable de ne pas considérer les déclarations courtes (ou, dans ce cas, les conclusions subjectives) comme toujours vraies. L'importation depuis __init__.pypeut être utile parfois, mais pas toujours.
Tobias Sette

2
Ces codes sont-ils exécutés au moment de l'importation ou de l'exécution?
user1559897

111

Le __init__.pyfichier fait que Python traite les répertoires le contenant comme des modules.

En outre, il s'agit du premier fichier à être chargé dans un module, vous pouvez donc l'utiliser pour exécuter le code que vous souhaitez exécuter à chaque fois qu'un module est chargé, ou spécifier les sous-modules à exporter.


89

Depuis Python 3.3, il __init__.pyn'est plus nécessaire de définir les répertoires en tant que packages Python importables.

Vérifiez PEP 420: packages implicites d'espace de noms :

Prise en charge native des répertoires de packages qui ne nécessitent pas de __init__.pyfichiers de marqueurs et peuvent s'étendre automatiquement sur plusieurs segments de chemin (inspirés de diverses approches tierces des packages d'espace de noms, comme décrit dans PEP 420 )

Voici le test:

$ mkdir -p /tmp/test_init
$ touch /tmp/test_init/module.py /tmp/test_init/__init__.py
$ tree -at /tmp/test_init
/tmp/test_init
├── module.py
└── __init__.py
$ python3

>>> import sys
>>> sys.path.insert(0, '/tmp')
>>> from test_init import module
>>> import test_init.module

$ rm -f /tmp/test_init/__init__.py
$ tree -at /tmp/test_init
/tmp/test_init
└── module.py
$ python3

>>> import sys
>>> sys.path.insert(0, '/tmp')
>>> from test_init import module
>>> import test_init.module

références:
https://docs.python.org/3/whatsnew/3.3.html#pep-420-implicit-namespace-packages
https://www.python.org/dev/peps/pep-0420/
Is __init__. py non requis pour les packages en Python 3?


3
C'est un package "namespace". Ne l'utilisez pas pour un emballage régulier.
méthane

@methan, pourriez-vous développer votre commentaire?
Robert Lugg


57

En Python, la définition de package est très simple. Comme Java, la structure hiérarchique et la structure de répertoires sont les mêmes. Mais vous devez avoir __init__.pydans un paquet. Je vais expliquer le __init__.pyfichier avec l'exemple ci-dessous:

package_x/
|--  __init__.py
|--    subPackage_a/
|------  __init__.py
|------  module_m1.py
|--    subPackage_b/
|------  __init__.py
|------  module_n1.py
|------  module_n2.py
|------  module_n3.py

__init__.pypeut être vide, tant qu'il existe. Il indique que le répertoire doit être considéré comme un package. Bien sûr, __init__.pypeut également définir le contenu approprié.

Si nous ajoutons une fonction dans module_n1:

def function_X():
    print "function_X in module_n1"
    return

Après l'exécution:

>>>from package_x.subPackage_b.module_n1 import function_X
>>>function_X()

function_X in module_n1 

Ensuite, nous avons suivi le package de hiérarchie et appelé module_n1 la fonction. Nous pouvons utiliser __init__.pydans subPackage_b comme ceci:

__all__ = ['module_n2', 'module_n3']

Après l'exécution:

>>>from package_x.subPackage_b import * 
>>>module_n1.function_X()

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named module_n1

Par conséquent, en utilisant * l'importation, le package du module est soumis au __init__.pycontenu.


À quoi ressemblera mon setup.py pour effectuer la même importation via la bibliothèque empaquetée? from package_x.subPackage_b.module_n1 import function_X
technazi

donc la clé à retenir ici est "en utilisant * l'importation, le package du module est soumis au contenu init .py"
Minnie

54

Bien que Python fonctionne sans __init__.pyfichier, vous devez toujours en inclure un.

Il spécifie qu'un paquet doit être traité comme un module, alors incluez-le donc (même s'il est vide).

Il y a aussi un cas où vous pouvez réellement utiliser un __init__.pyfichier:

Imaginez que vous ayez la structure de fichiers suivante:

main_methods 
    |- methods.py

Et methods.pycontenait ceci:

def foo():
    return 'foo'

Pour l'utiliser, foo()vous auriez besoin de l'un des éléments suivants:

from main_methods.methods import foo # Call with foo()
from main_methods import methods # Call with methods.foo()
import main_methods.methods # Call with main_methods.methods.foo()

Peut-être y avez-vous besoin (ou voulez-vous) de rester à l' methods.pyintérieur main_methods(runtimes / dépendances par exemple) mais vous voulez seulement importer main_methods.


Si vous avez changé le nom de methods.pyen, __init__.pyvous pouvez l'utiliser foo()en important simplement main_methods:

import main_methods
print(main_methods.foo()) # Prints 'foo'

Cela fonctionne car __init__.pyest traité comme faisant partie du package.


Certains packages Python le font. Un exemple est avec JSON , où l'exécution import jsonest en fait une importation à __init__.pypartir du jsonpackage ( voir la structure du fichier du package ici ):

Code source: Lib/json/__init__.py


39

__init__.py traitera le répertoire dans lequel il se trouve comme un module chargeable.

Pour les personnes qui préfèrent lire le code, je mets ici le commentaire de Two-Bit Alchemist .

$ find /tmp/mydir/
/tmp/mydir/
/tmp/mydir//spam
/tmp/mydir//spam/__init__.py
/tmp/mydir//spam/module.py
$ cd ~
$ python
>>> import sys
>>> sys.path.insert(0, '/tmp/mydir')
>>> from spam import module
>>> module.myfun(3)
9
>>> exit()
$ 
$ rm /tmp/mydir/spam/__init__.py*
$ 
$ python
>>> import sys
>>> sys.path.insert(0, '/tmp/mydir')
>>> from spam import module
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named spam
>>> 

30

Il facilite l'importation d'autres fichiers python. Lorsque vous avez placé ce fichier dans un répertoire (disons stuff) contenant d'autres fichiers py, vous pouvez faire quelque chose comme importer stuff.other.

root\
    stuff\
         other.py

    morestuff\
         another.py

Sans cela __init__.pyà l'intérieur du répertoire, vous ne pourriez pas importer other.py, car Python ne sait pas où se trouve le code source du contenu et ne peut pas le reconnaître en tant que package.


2
J'ai la même structure dans mon projet (python 3.4) mais je ne peux pas faire un autre.py voir other.py. Comment dois-je effectuer l'importation? de root.stuff importer autre? Il fonctionne en mode débogage VSCode mais pas en ligne de commande. Des idées?
rodrigorf

10

Un __init__.pyfichier facilite les importations. Quand un __init__.pyest présent dans un package, la fonction a()peut être importée à partir d'un fichier b.pycomme ceci:

from b import a

Sans cela, cependant, vous ne pouvez pas importer directement. Vous devez modifier le chemin du système:

import sys
sys.path.insert(0, 'path/to/b.py')

from b import a

que voulez-vous dire par "la fonction a () peut être importée à partir du fichier b.py [extrait] Sans elle, cependant, vous ne pouvez pas importer directement. "? Je peux importer la fonction a () du fichier b.py sans __init__.py.
aderchox
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.