Do czego służy __init__.py?

Odpowiedzi:

1452

Kiedyś była to wymagana część pakietu ( stary „zwykły pakiet” wcześniejszy niż 3.3 , a nie nowszy „pakiet przestrzeni nazw” w wersji 3.3+ ).

Oto dokumentacja.

Python definiuje dwa typy pakietów, zwykłe pakiety i pakiety przestrzeni nazw. Zwykłe pakiety to tradycyjne pakiety, takie jakie istniały w Pythonie 3.2 i wcześniejszych. Zwykły pakiet jest zwykle implementowany jako katalog zawierający __init__.pyplik. Po zaimportowaniu zwykłego pakietu __init__.pyplik ten jest domyślnie wykonywany, a definiowane przez niego obiekty są powiązane z nazwami w przestrzeni nazw pakietu. __init__.pyPlik może zawierać ten sam kod Pythona, że każdy inny moduł może zawierać i Python będzie dodać kilka dodatkowych atrybutów w module, gdy jest importowany.

Ale wystarczy kliknąć link, zawiera on przykład, więcej informacji i objaśnienie pakietów przestrzeni nazw, takich rodzajów pakietów bez __init__.py.

Loki
źródło
187
Co to znaczy: „ma to na celu zapobieżenie przypadkowemu ukryciu przez katalogi o wspólnej nazwie, takiej jak łańcuch znaków, prawidłowych modułów pojawiających się później na ścieżce wyszukiwania modułów”?
Carl G
97
@CllG Python przeszukuje listę katalogów w celu rozpoznania nazw, np. Instrukcji importu. Ponieważ mogą to być dowolne katalogi, a użytkownik końcowy może dodać dowolne, programiści muszą się martwić o katalogi, które mogą dzielić nazwę z prawidłowym modułem Pythona, takim jak „string” w przykładzie docs. Aby temu zaradzić, ignoruje katalogi, które nie zawierają pliku o nazwie _ _ init _ _.py (bez spacji), nawet jeśli jest pusty.
Two-Bit Alchemist
186
@CarlG Wypróbuj to. Utwórz katalog o nazwie „datetime”, w którym utworzą dwa puste pliki, plik init.py (z podkreśleniami) i datetime.py. Teraz otwórz interpreter, zaimportuj system i wydaj sys.path.insert(0, '/path/to/datetime'), zastępując tę ​​ścieżkę ścieżką do dowolnego właśnie utworzonego katalogu. Teraz spróbuj czegoś takiego from datetime import datetime;datetime.now(). Powinieneś dostać AttributeError (ponieważ teraz importuje twój pusty plik). Jeśli powtórzyłbyś te kroki bez tworzenia pustego pliku inicjującego, tak by się nie stało. Właśnie temu ma zapobiegać.
Two-Bit Alchemist
4
@ DarekNędza Masz coś niepoprawnie skonfigurowanego, jeśli nie możesz po prostu otworzyć interpretera języka Python i wydać go from datetime import datetimebezbłędnie. To dobrze, aż do wersji 2.3!
Two-Bit Alchemist
5
@SWang: To nieprawda: builtinswyświetla listę wbudowanych funkcji i klas , a nie wbudowanych modułów (por. Docs.python.org/3/tutorial/modules.html#the-dir-function ). Jeśli chcesz wyświetlić listę wbudowanych modułów , wykonaj import sys; print(sys.builtin_module_names)(por. Docs.python.org/3/library/sys.html#sys.builtin_module_names ).
Maggyero,
842

Nazwane pliki __init__.pysłużą do oznaczania katalogów na dysku jako katalogów pakietów Pythona. Jeśli masz pliki

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

i mydirjest na twojej drodze, możesz zaimportować kod module.pyjako

import spam.module

lub

from spam import module

Jeśli usuniesz __init__.pyplik, Python nie będzie już szukał podmodułów w tym katalogu, więc próby zaimportowania modułu zakończą się niepowodzeniem.

__init__.pyPlik jest zwykle pusty, ale może być używany do eksportowania wybranych fragmentów pakietu pod nazwą bardziej wygodny, funkcje convenience ładowni itp Biorąc pod uwagę powyższy przykład, zawartość modułu inicjującego mogą być dostępne jako

import spam

na tej podstawie

caritos
źródło
96
Aktualizacja: Plik __init__.pybył wymagany w Pythonie 2.X i jest nadal wymagany w Pythonie 2.7.12 (przetestowałem go), ale nie jest już wymagany od (rzekomo) Python 3.3 i nie jest wymagany w Pythonie 3.4.3 (I przetestowałem to). Aby uzyskać więcej informacji, zobacz stackoverflow.com/questions/37139786 .
Rob_before_edits
4
Nie używaj tego. Jest to pakiet „przestrzeni nazw”, a nie zwykły pakiet. pakiet przestrzeni nazw jest używany w bardzo rzadkich przypadkach użycia. Być może nie będziesz musiał wiedzieć, kiedy go używać. Po prostu użyj __init__.py.
metan
2
jednak jeśli masz setup.pyi używasz, musisz find_packages()mieć __init__.pyw każdym katalogu. Zobacz stackoverflow.com/a/56277323/7127824
techkuz
483

Oprócz oznaczania katalogu jako pakietu Pythona i definiowania __all__, __init__.pypozwala zdefiniować dowolną zmienną na poziomie pakietu. Jest to często wygodne, jeśli pakiet definiuje coś, co będzie często importowane, w sposób podobny do API. Ten wzór promuje przestrzeganie filozofii Pythona, że ​​„mieszkanie jest lepsze niż zagnieżdżone”.

Przykład

Oto przykład z jednego z moich projektów, w którym często importuję sessionmakerwywołanie w Sessioncelu interakcji z moją bazą danych. Napisałem pakiet „bazy danych” z kilkoma modułami:

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

Mój __init__.pyzawiera następujący kod:

import os

from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine

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

Ponieważ Sessiontutaj definiuję , mogę rozpocząć nową sesję przy użyciu poniższej składni. Ten kod byłby tak samo wykonywany wewnątrz lub na zewnątrz katalogu pakietu „bazy danych”.

from database import Session
session = Session()

Oczywiście jest to niewielka wygoda - alternatywą byłoby zdefiniowanie Sessionw nowym pliku takim jak „create_session.py” w moim pakiecie bazy danych i rozpoczęcie nowych sesji przy użyciu:

from database.create_session import Session
session = Session()

Dalsza lektura

Istnieje całkiem interesujący wątek reddit obejmujący odpowiednie zastosowania __init__.pytutaj:

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

Wydaje się, że większość uważa, że __init__.pypliki powinny być bardzo cienkie, aby uniknąć naruszenia filozofii „jawne jest lepsze niż dorozumiane”.

Nathan Gould
źródło
3
engine, sessionmaker, create_engine, I oswszystkie mogą być również importowane z databaseteraz ... Wygląda na to zrobiłeś bałagan tej przestrzeni nazw.
ArtOfWarfare
9
@ArtOfWarfare, możesz użyć, __all__ = [...]aby ograniczyć liczbę importowanych plików import *. Ale poza tym tak, masz nieporządną przestrzeń nazw najwyższego poziomu.
Nathan Gould,
Czy mogę wiedzieć, jaki jest „URL BAZY DANYCH”? Próbowałem to zreplikować, dołączając silnik create_ do 'mysql + mysqldb: // root: python @ localhost: 3306 / test', ale to nie działa. Dzięki.
SunnyBoiz
2
Jak uzyskasz dostęp do klasy „Sesja” zdefiniowanej w init od wewnątrz pakietu, np. Quieries.py?
vldbnc
252

Są 2 główne powody __init__.py

  1. Dla wygody: inni użytkownicy nie będą musieli znać dokładnej lokalizacji Twoich funkcji w hierarchii pakietów.

    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

    wtedy inni mogą wywołać add () przez

    from your_package import add

    bez znajomości file1, na przykład

    from your_package.file1 import add
  2. Jeśli chcesz coś zainicjować; na przykład rejestrowanie (które należy umieścić na najwyższym poziomie):

    import logging.config
    logging.config.dictConfig(Your_logging_config)
flycee
źródło
6
och, zanim przeczytałem twoją odpowiedź, pomyślałem, że wywołanie funkcji wprost z jej lokalizacji jest dobrą praktyką.
Aerin
2
@Aerin lepiej byłoby nie uważać krótkich stwierdzeń (lub w tym przypadku subiektywnych wniosków) za zawsze prawdziwe. Importowanie z __init__.pymoże być przydatne czasem, ale nie zawsze.
Tobias Sette,
2
Czy te kody są wykonywane podczas importu lub w czasie wykonywania?
user1559897
111

__init__.pyPlik sprawia Python katalogów zawierających je traktować jako moduły.

Ponadto jest to pierwszy plik ładowany do modułu, więc można go użyć do wykonania kodu, który chcesz uruchomić za każdym razem, gdy moduł jest ładowany, lub określić submoduły, które mają zostać wyeksportowane.

Czy Berk Güder
źródło
89

Od wersji Python 3.3 __init__.pynie jest już wymagane definiowanie katalogów jako importowalnych pakietów Pythona.

Sprawdź PEP 420: Pakiety niejawnej przestrzeni nazw :

Natywne wsparcie dla katalogów pakietów, które nie wymagają __init__.py plików znaczników i mogą automatycznie obejmować wiele segmentów ścieżek (inspirowane różnymi podejściami stron trzecich do pakietów przestrzeni nazw, jak opisano w PEP 420 )

Oto 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

referencje:
https://docs.python.org/3/whatsnew/3.3.html#pep-420-implicit-namespace-packages
https://www.python.org/dev/peps/pep-0420/
Czy __init__. py nie jest wymagane dla pakietów w Pythonie 3?

zeekvfu
źródło
3
Jest to pakiet „przestrzeni nazw”. Nie używaj go do zwykłego opakowania.
metan
@methan, czy mógłbyś rozwinąć swój komentarz?
Robert Lugg,
3
@RobertLugg See dev.to/methane/don-t-omit-init-py-3hga
metan
57

W Pythonie definicja pakietu jest bardzo prosta. Podobnie jak Java, struktura hierarchiczna i struktura katalogów są takie same. Ale musisz mieć __init__.pyw pakiecie. Wyjaśnię __init__.pyplik na poniższym przykładzie:

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__.pymoże być pusty, o ile istnieje. Wskazuje, że katalog należy traktować jako pakiet. Oczywiście,__init__.py można również ustawić odpowiednią treść.

Jeśli dodamy funkcję w module_n1:

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

Po bieganiu:

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

function_X in module_n1 

Następnie postępowaliśmy zgodnie z pakietem hierarchii i nazwaliśmy moduł_n1 funkcją. Możemy użyć __init__.pyw subPackage_b w następujący sposób:

__all__ = ['module_n2', 'module_n3']

Po bieganiu:

>>>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

Dlatego przy użyciu * importowania pakiet modułów jest zależny od __init__.pyzawartości.

Marcus Thornton
źródło
Jak mój setup.py będzie wyglądał, aby wykonać ten sam import przez spakowaną bibliotekę? from package_x.subPackage_b.module_n1 import function_X
technazi
więc kluczem jest tutaj: „przy użyciu * importowania, pakiet modułów podlega inicjalizacji .py zawartości”
Minnie
54

Chociaż Python działa bez __init__.pypliku, nadal powinieneś go dołączyć.

Określa, że ​​pakiet powinien być traktowany jako moduł, dlatego dołącz go (nawet jeśli jest pusty).

Istnieje również przypadek, w którym możesz faktycznie użyć __init__.pypliku:

Wyobraź sobie, że masz następującą strukturę plików:

main_methods 
    |- methods.py

I methods.pyzawierał to:

def foo():
    return 'foo'

Aby go użyć foo(), potrzebujesz jednego z poniższych:

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()

Być może potrzebujesz (lub chcesz) pozostać w methods.pyśrodku main_methods(na przykład środowiska wykonawcze / zależności), ale chcesz tylko zaimportować main_methods.


Jeśli zmieniłeś nazwę methods.pyna, __init__.pymożesz użyć foo(), importując main_methods:

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

Działa __init__.pyto, ponieważ jest traktowane jako część pakietu.


Niektóre pakiety Pythona faktycznie to robią. Przykładem jest JSON , w którym uruchomiony import jsonjest import __init__.pyz jsonpakietu ( zobacz tutaj strukturę pliku pakietu ):

Kod źródłowy: Lib/json/__init__.py

Szymon
źródło
39

__init__.py potraktuje katalog, w którym się znajduje, jako moduł do załadowania.

Dla osób, które wolą czytać kod, zamieszczam tutaj komentarz 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
>>> 
B.Mr.W.
źródło
30

Ułatwia importowanie innych plików Pythona. Kiedy umieścisz ten plik w katalogu (powiedzmy rzeczy) zawierającym inne pliki py, możesz zrobić coś takiego jak import rzeczy. Inne.

root\
    stuff\
         other.py

    morestuff\
         another.py

Bez tego __init__.pywewnątrz katalogu nie można zaimportować pliku other.py, ponieważ Python nie wie, gdzie jest kod źródłowy rzeczy i nie jest w stanie rozpoznać go jako pakietu.

Epitafium
źródło
2
Mam taką samą strukturę w moim projekcie (python 3.4), ale nie jestem w stanie zrobić pliku another.py zobacz other.py. Jak powinienem dokonać importu? z root.stuff importować inne? Działa w trybie debugowania VSCode, ale nie w wierszu poleceń. Jakieś pomysły?
rodrigorf
10

__init__.pyPlik sprawia importu łatwe. Gdy __init__.pyw pakiecie jest obecny, funkcję a()można zaimportować z pliku w następujący b.pysposób:

from b import a

Bez niego nie można jednak importować bezpośrednio. Musisz zmienić ścieżkę systemową:

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

from b import a
Alec Alameddine
źródło
co masz na myśli: „ funkcję a () można importować z pliku b.py [snippet] Bez tego nie można jednak importować bezpośrednio. ”? Mogę zaimportować funkcję a () z pliku b.py bez __init__.py.
aderchox