Qu'est-ce que __init__.py de Python?

Lorsque vous commencez à utiliser Python, vous souhaitez gérer vos fichiers dans une hiérarchie de répertoires. C'est là que le fichier __init __. Py apparaît.

Pour l'amour de Dieu, qui est-ce? Il y a beaucoup d'informations, mais je ne trouve rien qui les explique d'une manière qui ait du sens. Même dans la documentation Python, il est difficile de savoir à quoi se référer pour la bonne réponse [^ Note 1].

[^ Note 1]: Il semble que la lecture de "Python Tutorial-Modules" est la bonne réponse (pour la version que vous utilisez). Voir la documentation correspondante). En tant que tutoriel, lorsque je l'ai lu pour la première fois, je ne pouvais pas du tout le comprendre et je suis entré dans ma mémoire.

Donc, j'ai résumé __init __. Py. (C'est un peu long) Puisqu'il est écrit dans un format de lecture, si vous voulez voir seulement la conclusion ("[ __init __. Py role](# __ init__py- role) "), faites défiler jusqu'à la dernière. L'exemple de code python utilise principalement 3.6 / 3.5 [^ Note 2].

[^ Note 2]: Afin de réduire le bruit, les shebangs tels que #! / Usr / bin / env python et - * - codage: utf-8 - * - etc. de la spécification de code de caractère sont exclus. Par conséquent, seule la partie centrale est décrite simplement.

  1. "Module", "Package" et "Espace de noms"
  2. Modules et structure hiérarchique
  3. Module de fichier unique
  4. Structure hiérarchique et espace de noms par répertoire
  5. Mappage des répertoires et des espaces de noms
  6. [Rôle de __init __. Py](Rôle de # __init__py-)
  7. Marqueur de recherche de module
  8. Initialisation de l'espace de noms
  9. Définition de la cible de l'importation de caractères génériques (définition de «all»)
  10. Définition des espaces de noms pour les autres modules du même répertoire
  11. Résumé
  12. Notes sur unittest (ajoutées par Commentaire de @methane)

"Module", "Package" et "Espace de noms"

Avant d'entrer dans le sujet principal, brièvement sur les "modules", "packages" et "espaces de noms".

[^ Note 3]: "Termes généraux Python" dit "Réécrire le code en Python" L'unité de base à utiliser: c'est-à-dire un ensemble de codes importés d'un autre code. "

Glossary: module An object that serves as an organizational unit of Python code. Modules have a namespace containing arbitrary Python objects. Modules are loaded into Python by the process of importing.

Voir aussi: "Python Documentation-Tutorial- Modules"

[^ Note 4]: Dans "Python documentation-Tutorial- Packages", "Modules contenant d'autres modules" «Il a été décrit comme.

Glossary: package A Python module which can contain submodules or recursively, subpackages. Technically, a package is a Python module with an __path__ attribute.

Glossary: namespace The place where a variable is stored. Namespaces are implemented as dictionaries. There are the local, global and built-in namespaces as well as nested namespaces in objects (in methods). Namespaces support modularity by preventing naming conflicts. For instance, the functions builtins.open and os.open() are distinguished by their namespaces. Namespaces also aid readability and maintainability by making it clear which module implements a function. For instance, writing random.seed() or itertools.islice() makes it clear that those functions are implemented by the random and itertools modules, respectively.

Exemple d'espace de noms 1


import alpha.bravo.charlie

alpha.bravo.charlie.delta()

Dans cet exemple (Exemple 1), l'espace de noms est structuré hiérarchiquement comme ʻalphabravo charlie, et la procédure (fonction) appelée delta () ʻimplémentée dans ce charlie. Appelle.

Vous pouvez comprendre intuitivement que si les espaces de noms supérieurs sont différents, même si les espaces de noms inférieurs ont le même nom, ce sont des éléments différents.

Exemple d'espace de noms 2


import os.path

os.path.join('/home', 'user')

Voici (Exemple 2) un exemple couramment utilisé de jonction de chemins, appelant join () dans l'espace de noms ʻospath`.

Modules et structure hiérarchique

Module de fichier unique

Préparez deux fichiers dans le même répertoire.

organisation des fichiers


./
├─ module01.py .....module
└─ sample0010.py ...Dossier d'exécution(= Module principal)

module1.py


def hello():
    print( "Hello, world!" )

sample0010.py


import module01
module01.hello()

sample0010.Exécuter py


$ python3 sample0010.py
Hello, world!
$ python2 sample0010.py
Hello, world!

Les modules de fichiers uniques peuvent être «importés» en les plaçant dans le même répertoire. Si vous souhaitez simplement séparer les fichiers, vous n'avez pas besoin de créer un répertoire.

Pour le moment, «__init __. Py» n'est pas nécessaire. Le nom du fichier est «module01.py», mais le nom du module est «module01» (nom de fichier moins .py). L'espace de noms est «module01».

Structure hiérarchique et espace de noms par répertoire

Considérez la structure de répertoires et de fichiers suivante.

Échantillon 2 Structure hiérarchique


./
├─ sample0020.py .....Dossier d'exécution
└─ dir/
    └─ module02.py ...module

dir/module02.py


def hello():
    print( "Hello, world! from module02" )

A ce moment, pour appeler module02, dans sample0020.py, il est nécessaire d'écrire comme suit.

sample0020.py


import dir.module02
dir.module02.hello()

Résultat d'exécution


$ python3 sample0020.py
Hello, world! from module02

$ python2 sample0020.py
Traceback (most recent call last):
  File "sample0020.py", line 1, in <module>
    import dir.module02
ImportError: No module named dir.module02

Cela a fonctionné comme prévu dans python3, mais j'ai eu une erreur dans python2. C'est parce que nous avons besoin de __init __. Py sous dir.

Depuis la v3.3, il peut être appelé même s'il n'y a pas de __init __. Py dans le répertoire où se trouve le module appelant, mais dans le" paquet normal ", __init __. Py doit être placé.

What’s New In Python 3.3: PEP 420: Implicit Namespace Packages Native support for package directories that don’t require __init__.py marker files and can automatically span multiple path segments (inspired by various third party approaches to namespace packages, as described in PEP 420)

Il s'agit d'une fonctionnalité pour les nouveaux "Packages d'espace de noms" ajoutés dans la V3.3, qui nécessite __init __. Py pour les" packages réguliers ".

Regular packages A regular package is typically implemented as a directory containing an init.py file.

A moins que vous ne créiez des "packages d'espace de noms", vous devez mettre __init __. Py dans le répertoire où vous placez les modules.

Voyons maintenant la signification de «__init __. Py».

Mappage de répertoires et d'espaces de noms

Dans l'exemple 2, l'appel de module02 devait être référencé dans l'espace de noms dir.module02 car il y avait un répertoire appelédir /. dir est sur le chemin. Il n'a aucune substance, mais doit être spécifié comme une hiérarchie d'espaces de noms.

C'est là qu'intervient «__init __. Py». La méthode pour appeler directement avec le nom module02 après l'avoir intégré dans la hiérarchie des répertoires est __init __. Py.

Vous pouvez nommer le répertoire module02 / au lieu de dir /, mais le fichier que vous appelez doit finir par être module02. What .hello (). Par conséquent, le fichier «__init __. Py» a une signification spéciale afin qu'il puisse être traité comme un module avec le même espace de noms que le nom du répertoire.

En d'autres termes, en écrivant un programme dans module02 / __ init __. Py au lieu de dir / module02.py, vous pouvez l'appeler comme module02.

Échantillon 3&nbsp;__init__.Structure hiérarchique, y compris py


./
├─ sample0030.py .....Dossier d'exécution
└─ module02/
    └─ __init__.py ... "module02"Entité de

module02/__init__.py


def hello():
    print( "Hello, world! from __init__.py" )

sample0030.py


import module02
module02.hello()

Résultat d'exécution


$ python2 sample0030.py
Hello, world! from __init__.py

$ python3 sample0030.py
Hello, world! from __init__.py

Je ne connais pas le contexte historique (je ne l'ai pas étudié), mais je soupçonne que c'était l'original __init __. Py. (Spéculation de l'auteur) Il y a un endroit pour écrire __init__ () dans la classe (espace de noms) du module qui existe en tant que fichier, mais il n'y a pas de place pour écrire __init__ () dans l'espace de noms qui existe en tant que répertoire. Il semble qu'il soit composé de ce fichier appelé «__init __. Py».

Et puisque «__init __. Py» représente l'existence de l'espace de noms, «__init __. Py» aurait été utilisé comme marqueur pour le module. Par conséquent, l'implémentation était que le répertoire contenant les fichiers à lire en tant que module devait avoir __init __. Py (= le répertoire contenant __init __. Py fait partie de l'espace de noms explicite). Je l'imagine.

Si vous mettez «__init __. Py», il est traité comme un module d'entité avec le même espace de noms que le nom du répertoire. En d'autres termes, «__init __. Py» est un fichier pour mapper les noms de répertoire en tant que noms de module (ou «espaces de noms» explicites). (Rôle du constructeur lorsque le nom du répertoire est utilisé comme espace de noms)

Si vous pouvez comprendre cela, vous constaterez peut-être que __init __. Py, que vous ne compreniez pas jusqu'à présent, devient un peu plus familier.

Rôle de __init __. Py

Après avoir compris ce qui précède, si vous lisez «[Module]» (https://docs.python.org/ja/3.6/tutorial/modules.html) du didacticiel Python, ce sera «__init __. Py». Je pense que le rôle de est facile à comprendre.

  1. __init __. Py est un marqueur pour la recherche de module.
  2. __init __. Py initialise l'espace de noms nommé d'après le répertoire dans lequel il réside.
  3. __init __. Py définit également la cible de l'importation de caractères génériques dans l'espace de noms (définition de __all__).
  4. __init __. Py définit les espaces de noms pour les autres modules du même répertoire.

2 . ~ 4 . Peut être regroupé comme "initialisation du module ou du package", mais je les ai divisés ici.

1. Marqueur de recherche de module

«__init __. py» est utilisé comme marqueur pour rechercher des modules dans la hiérarchie. «__Init __. Py» doit exister pour que les modules hiérarchisés par répertoire soient chargés. (Nous ne couvrirons pas les "Packages d'espace de noms" qui ne nécessitent pas __init __. Py)

Regular packages Python defines two types of packages, regular packages and namespace packages. Regular packages are traditional packages as they existed in Python 3.2 and earlier. A regular package is typically implemented as a directory containing an __init__.py file.

2. Initialisation de l'espace de noms

Comme nous l'avons déjà vu, lorsque vous traitez un nom de répertoire comme un module d'espace de noms, enregistrez le contenu à exécuter en premier dans __init __. Py. Même s'il s'agit d'une ʻimport du module inférieur, le module inférieur est exécuté après l'initialisation lorsque l'espace de noms supérieur est exécuté. Il convient de noter que si une initialisation est requise avant d'exécuter (charger) le module inférieur, elle doit être effectuée avant ʻimport le module inférieur.

3. Définition de la cible de l'importation de caractères génériques (définition de «all»)

Bien qu'il soit décrit dans "Python Tutorial- Import \ * from Package" La liste __all__ définit ce qui est importé lorsque vous appelez depuis mon_module import *.

Ceci n'est pas limité à __init __. Py, mais peut être défini dans tous les modules.

Voir l'exemple 4 ci-dessous. Avoir deux fichiers de script Python dans le même répertoire.

Échantillon 4


./
├─ sample0040.py ...Dossier d'exécution
└─ module04.py .....module

sample0040.py


from module04 import *

hello1()
hello2()
hello3()

Dans sample0040.py, * ʻest utilisé pour ʻimport, commefrom module04 import *. C'est un programme simple qui appelle hello1 (), hello2 () et hello3 () dans l'ordre après ʻimport`.

module04.py


__all__ = ['hello1', 'hello2']

def hello1():
    print( "Hello, this is hello1" )

def hello2():
    print( "Hello, this is hello2" )

def hello3():
    print( "Hello, this is hello3" )

Dans module04.py,hello1 (),hello2 ()et hello3 () sont définis. La liste «all» ne contient que «bonjour1» et «bonjour2», pas «bonjour3».

Le résultat de l'exécution est le suivant.

Résultat d'exécution


$ python sample0040.py
Hello, this is hello1
Hello, this is hello2
Traceback (most recent call last):
  File "sample0040.py", line 5, in <module>
    hello3()
NameError: name 'hello3' is not defined

L'appel à hello3 () n'était pas défini et a entraîné l'erreur "NameError: name'hello3 'is not defined". C'est parce qu'il n'est pas dans la liste de «all». Ce n'est pas que hello3 () est caché, c'est juste le comportement quand ʻimport * est utilisé. Comme test, vous pouvez également appeler hello3 () par ʻimport sans utiliser * ʻet appeler explicitement module04`.

sample0041.py


import module04

module04.hello1()
module04.hello2()
module04.hello3()

Résultat d'exécution


$ python sample0041.py
Hello, this is hello1
Hello, this is hello2
Hello, this is hello3

La définition de __all__ dans __init __. Py définit uniquement un objet qui peut être référencé lorsqu'un module dont l'espace de nom est un nom de répertoire est ʻimport avec *`.

4. Définition des espaces de noms pour les autres modules du même répertoire

J'ai écrit ci-dessus que vous pouvez l'appeler comme un module avec le même nom que le nom du répertoire en définissant une fonction, etc. dans __init __. Py. Au fur et à mesure que «__init __. Py» grossit, vous voudrez écrire uniquement l'initialisation dans «__init __. Py» et garder le fichier hors.

Essayez-le avec le répertoire et la structure de fichiers suivants.

Échantillon 5


./
├─ sample0050.py ......Dossier d'exécution
└─ module05
    ├─ __init__.py .... "module05"Fichier d'initialisation
    ├─ _module05.py ... "module05"Entité de
    └─ module06.py .... "module05"Modules supplémentaires

On suppose que «module05 / _module05.py» a été développé pour être fourni en tant que «module05» depuis le début, ou qu'il a été retiré parce que «__init __. Py» était gonflé. J'ai ajouté un trait de soulignement (_) pour lui donner le même nom de fichier que le répertoire afin qu'il puisse être vu comme le nom du module réel.

python:./module05/_module05.py


print( "in _module05.py" )

def hello(caller=""):
    print( "Hello, world! in _module05 called by {}".format(caller) )

On suppose que module05 / module06.py est un fichier qui a été développé en dehors de __init __. Py depuis le début.

python:./module05/module06.py


print( "in module06.py" )

def hello(caller=""):
    print( "Hello, world! in module06 called by {}".format(caller) )

Hello () in _module05.py et hello () in module06.py passent un appelant comme argument pour que l'appelant sache.

Au fait, c'est «__init __. Py», mais comme il charge le module dans le même répertoire, le point («.») Représentant le répertoire courant (même espace de noms) est ajouté au début de «_module05» et «module06». De plus, comme les noms de hello () sont en conflit, nous les renommons en utilisant ʻas`.

python:./module05/__init__.py


print( "in __init__.py" )

# import _module05.hello() as hello05() in the same directory
from ._module05 import hello as hello05
# import module06.hello() as hello06() in the same directory
from .module06 import hello as hello06

__all__ = ['hello05', 'hello06']

# Do initialize something bellow
hello05("__init__.py")
hello06("__init__.py")

Sous la définition de l'objet qui peut être appelé * par __all__, # Initialisez quelque chose ci-dessous, il est supposé qu'une initialisation est en cours.

L'appel d'origine «sample0050.py» est le suivant. Avec from module05 import *, seul le module de module05 est chargé.

python:./sample0050.py


print( "in {} 1".format( __file__ ) )

from module05 import *

print( "in {} 2".format( __file__ ) )
hello05(__file__)
hello06(__file__)

Le résultat de l'exécution est le suivant.

Résultat d'exécution


$ python3 sample0050.py
in sample0050.py 1
in __init__.py
in _module05.py
in module06.py
Hello, world! in _module05 called by __init__.py
Hello, world! in module06 called by __init__.py
in sample0050.py 2
Hello, world! in _module05 called by sample0050.py
Hello, world! in module06 called by sample0050.py

Vous pouvez voir que module05 / _module05.py et module05 / module06.py sont appelés comme module05 par l'intervention de __init __. Py.

Au fait, module05 / module06.py n'est pas caché, vous pouvez donc l'appeler directement.

module05/module06.Appel direct à py


$ python3 -c "import module05.module06; module05.module06.hello('shell')"
in __init__.py
in _module05.py
in module06.py
Hello, world! in _module05 called by __init__.py
Hello, world! in module06 called by __init__.py
Hello, world! in module06 called by shell

Une fois que vous aurez compris cela, je pense que vous serez en mesure de développer des modules qui pourront être réutilisés en tant que «packages».

Résumé

J'ai confirmé le rôle de __init __. Py en le vérifiant.

  1. Vous avez besoin de «__init __. Py» pour importer un module en couches. (Je ne mentionnerai pas les "packages d'espace de noms implicites" qui n'installe pas __init __. Py ajouté dans la v3.3 ici)
  2. Le processus d'initialisation du module est décrit dans «__init __. Py».

Il est souvent écrit qu'il y a un rôle, mais ici, le second rôle est décrit en trois parties.

2-1. Initialisation de l'espace de noms 2-2. Définition de la cible du caractère générique ʻimport(définition deall`) 2-3. Définition des espaces de noms pour les autres modules du même répertoire

Troisièmement, il est possible de définir d'autres modules qui ne sont pas dans le même répertoire par ʻimport`, mais tout d'abord, c'est un module qui se trouve dans le même répertoire.

Concernant les instructions d'exécution décrites dans le module, il est indiqué que "Ces instructions d'exécution ne sont exécutées que lorsque le nom du module est trouvé pour la première fois dans l'instruction d'importation" ^ Note 5, Peu importe le nombre de fois que vous répétez ʻimport, il ne sera exécuté qu'une seule fois. (Si vous utilisez ʻimportlib et ʻimportlib.reload () `, il sera exécuté explicitement)

Après avoir lu cet article, je pense que si vous lisez à nouveau "Python Tutorial-Modules", vous obtiendrez une meilleure compréhension. Je vais.

J'espère que cela vous aide même un peu.

Remarques sur unittest

Ajouté par Commentaire de @methane (2020/01/20)

__init __. py agit comme un marqueur et est utilisé par unittest pour rechercher des modules de test sous la hiérarchie, en particulier dans notre environnement immédiat.

Un exemple est présenté ci-dessous.

arborescence d'environnement de test unittest


./
├─ my_module
│   ├─ __init__.py ............. my_Pour le mappage de modules
|   └─ _my_module.py ........... my_Le contenu du module
|
├─ tests-without__init__/ ...... __init__.Répertoire de test sans py
|   └─ unit/ ...................Hiérarchie pour le test unitaire
|       └─ test_my_module.py ...Programme d'essai
|
└─ tests-with__init__/ ......... __init__.Répertoire de test avec py
    └─ unit/ ...................Hiérarchie pour le test unitaire
        ├─ __init__.py .........Mettez simplement le fichier. Le contenu est vide.
        └─ test_my_module.py ... tests-without/unit/test_my_module.Lien symbolique vers py

Installez my_module / __ init __. Py et my_module / _my_module.py pour préparer my_module.

python:my_module/__init__.py&nbsp;-&nbsp;my_En tant que module_my_module.Charger py


__all__ = [
    'Sample',
    ]

from ._my_module import Sample

my_module/_my_module.py&nbsp;-&nbsp;my_Définir le contenu du module


#!/usr/bin/env python
# -*- coding: utf-8 -*-

import six

class Sample:
    def __init__(self):
        self.message = "__file__: " + __file__ + ", __name__:" + __name__
        self.saved_message = self.message

    def get(self):  #Cette cible de test
        return self.message

if __name__ == "__main__":
    sample = Sample()
    six.print_( sample.get() )

Préparez test_my_module.py sous la hiérarchie pour tester en utilisant unittest. Ne mettez pas __init __. Py sous tests-without__init__ / unit /.

tests-without__init__/unit/test_my_module.py


#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys, os

import unittest

#Variable d'environnement MY_MODULE_Lire PATH, sys.Définir sur chemin
module_dir = os.getenv( 'MY_MODULE_PATH', default=os.getcwd() )
sys.path.append( module_dir )

from my_module import Sample

class myClass(unittest.TestCase):
    global module_dir

    def setUp(self):
        self.moduledir = os.path.join( module_dir, "my_module" )
        self.modulefilepath = os.path.join( self.moduledir, "_my_module.py" )
        self.modulename = "my_module._my_module"
        self.sample = Sample()

    def tearDown(self):
        del self.sample

    # Sample.get()un test de
    def test_get(self):
        self.assertEqual( self.sample.get(), "__file__: " + self.modulefilepath + ", __name__:" + self.modulename )

if __name__ == "__main__":
    unittest.main()

Préparez test_my_module.py sous la hiérarchie pour tester en utilisant unittest. (En fait, c'est un lien symbolique, identique au contenu de tests-without__init__ / unit / test_my_module.py) Placez un fichier vide __init __. Py sous tests-with__init__ / unit /.

tests-with__init__/unit/__init__.py


# nothing here

tests-with__init__/unit/test_my_module.py


tests-without__init__/unit/test_my_module.Même contenu que py(Lien symbolique)

Exécutez le test. Tout d'abord, de la personne qui a «__init __. Py».

__init__.Résultat d'exécution avec py


$ python3 -m unittest discover tests-with__init__/ -v
test_get (unit.test_my_module.myClass) ... ok

----------------------------------------------------------------------
Ran 1 test in 0.001s

OK
$

Il dit Ran 1 test, j'ai trouvé le programme de test sous ʻunit /` et l'ai exécuté.

Par contre, si __init __. Py n'existe pas ...

__init__.Résultat d'exécution sans py


$ python3 -m unittest discover tests-without__init__/ -v

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK
$

Sans __init __. Py, il ne trouvera pas le programme de test sous ʻunit /` et le test ne fonctionnera pas.

Recommended Posts

Qu'est-ce que __init__.py de Python?
Qu'est-ce que l'espace de noms
Qu'est-ce que copy.copy ()
Qu'est-ce que Django? .. ..
Qu'est-ce que dotenv?
Qu'est-ce que POSIX
Qu'est-ce que Linux
Qu'est-ce que le klass?
Qu'est-ce que SALOME?
Qu'est-ce que Linux?
Qu'est-ce que python
Qu'est-ce que l'hyperopt?
Qu'est-ce que Linux
Qu'est-ce que pyvenv
Qu'est-ce que __call__
Qu'est-ce que Linux
Qu'est-ce que Python
Qu'est-ce qu'une distribution?
Qu'est-ce que le F-Score de Piotroski?
Qu'est-ce que Raspberry Pi?
[Python] Qu'est-ce que Pipeline ...
Qu'est-ce qu'un terminal?
Où est fluentd de python ??
[Tutoriel PyTorch ①] Qu'est-ce que PyTorch?
Qu'est-ce que le réglage des hyper paramètres?
Qu'est-ce que JSON? .. [Remarque]
Qu'est-ce qu'un pointeur?
Qu'est-ce que l'apprentissage d'ensemble?
Qu'est-ce que TCP / IP?
Qu'est-ce qu'un itérateur?
Qu'est-ce que UNIT-V Linux?
[Python] Qu'est-ce que virtualenv
Qu'est-ce que l'apprentissage automatique?
Qu'est-ce que l'analyse de régression logistique?
Quelle est la fonction d'activation?
La modernisation de l'orientation objet de Python est-elle?
Qu'est-ce qu'une variable d'instance?
Qu'est-ce qu'un arbre de décision?
Qu'est-ce qu'un changement de contexte?
Qu'est-ce que Google Cloud Dataflow?
[DL] Qu'est-ce que la décroissance du poids?
[Python] Python et sécurité-① Qu'est-ce que Python?
Qu'est-ce qu'un super utilisateur?
La programmation du concours, c'est quoi (bonus)
[Python] * args ** Qu'est-ce que kwrgs?
Qu'est-ce qu'un appel système
A quoi sert l'interface ...
Qu'est-ce que Project Euler 3 Acceleration?
Qu'est-ce qu'une fonction de rappel?
Qu'est-ce que la fonction de rappel?
Quel est votre "coefficient de Tanimoto"?
Cours de base Python (1 Qu'est-ce que Python)
Qu'est-ce que l'étiquetage dans les prévisions financières?
Qu'est-ce que la régression de crête de rang réduit?
Qu'est-ce que Azure Automation Update Management?
[Python] Qu'est-ce que @? (À propos des décorateurs)