Для чого це __init__.py?


Відповіді:


1453

Він раніше був обов'язковою частиною пакету ( старий, попередній 3.3 "звичайний пакет" , а не новіший 3.3+ "пакет простору імен" ).

Ось документація.

Python визначає два типи пакунків, звичайні пакети та пакети простору імен. Звичайні пакети - це традиційні пакунки, як вони існували в Python 3.2 та новіших версіях. Звичайний пакет зазвичай реалізується у вигляді каталогу, що містить __init__.pyфайл. Коли імпортується звичайний пакет, цей __init__.pyфайл неявно виконується, а об'єкти, які він визначає, пов'язані з іменами в просторі імен пакету. __init__.pyФайл може містити один і той же код Python , що будь-який інший модуль може містити, і Python буде додати деякі додаткові атрибути модуля , коли він імпортується.

Але просто натисніть посилання, воно містить приклад, більше інформації та пояснення пакетів простору імен, без виду пакунків __init__.py.


187
Що це означає: "це робиться для того, щоб каталоги із загальною назвою, наприклад, рядком, ненавмисно приховували дійсні модулі, які з’являються пізніше на шляху пошуку модуля"?
Карл Г

97
@CarlG Python здійснює пошук у списку каталогів для вирішення імен, наприклад, у імпортах операторів. Оскільки це може бути будь-яка директорія, а довільний їх може додати кінцевий користувач, розробникам доводиться турбуватися про каталоги, які мають спільне ім'я з дійсним модулем Python, наприклад, "string" у прикладі документів. Щоб полегшити це, він ігнорує каталоги, які не містять файл з ім'ям _ _ init _ _.py (немає пробілів), навіть якщо він порожній.
Двобітовий алхімік

186
@CarlG Спробуйте це. Створіть каталог під назвою 'datetime' і в ньому створіть два порожні файли, файл init.py (з підкресленнями) та datetime.py. Тепер відкрийте інтерпретатор, імпортуйте sys та видайте sys.path.insert(0, '/path/to/datetime'), замінивши цей шлях на шлях до каталогу, який ви щойно створили. Тепер спробуйте щось на кшталт from datetime import datetime;datetime.now(). Ви повинні отримати AttributeError (оскільки він імпортує ваш порожній файл зараз). Якби ви повторили ці кроки, не створюючи порожній файл init, цього не сталося. Ось що призначено запобігти.
Двобітовий алхімік

4
@ DarekNędza У вас щось встановлено неправильно, якщо ви не можете просто відкрити інтерпретатор Python і видавати from datetime import datetimeбез помилок. Це добре ще до версії 2.3!
Двобітовий алхімік

5
@SWang: Це неправильно: builtinsперераховує вбудовані функції та класи , а не вбудовані модулі (див. Docs.python.org/3/tutorial/modules.html#the-dir-function ). Якщо ви хочете перерахувати вбудовані модулі , зробіть це import sys; print(sys.builtin_module_names)(див. Docs.python.org/3/library/sys.html#sys.builtin_module_names ).
Маджієро

842

Названі файли __init__.pyвикористовуються для позначення каталогів на диску як каталогі пакетів Python. Якщо у вас є файли

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

і mydirна вашому шляху, ви можете імпортувати код в module.pyякості

import spam.module

або

from spam import module

Якщо ви вилучите __init__.pyфайл, Python більше не буде шукати підмодулі всередині цього каталогу, тому спроби імпорту модуля не вдасться.

__init__.pyФайл зазвичай порожній, але може бути використаний для експорту окремих частин пакета під зручнішим назвою, трюмні зручні функції і т.д. З огляду на наведену вище приклад, вміст модуля ініціалізації можна звертатися як

import spam

виходячи з цього


96
Оновлення: файл __init__.pyбув потрібний під Python 2.X і все ще потрібен під Python 2.7.12 (я тестував його), але він більше не потрібен від (нібито) Python 3.3 і далі, і не потрібен під Python 3.4.3 (I випробував це). Докладнішу інформацію див. У розділі stackoverflow.com/questions/37139786 .
Rob_before_edits

4
Не використовуйте його. Це пакет "простору імен", а не звичайний пакет. Пакет простору імен використовується для дуже рідкісних випадків використання. Можливо, вам не потрібно буде знати, коли ним користуватися. Просто використовуйте __init__.py.
метан

2
однак якщо у вас є setup.pyі ви використовуєте, find_packages()це потрібно мати __init__.pyу кожному каталозі. Див stackoverflow.com/a/56277323/7127824
techkuz

484

На додаток до мітки каталогу як пакету Python та визначення __all__, __init__.pyдозволяє визначити будь-яку змінну на рівні пакету. Це часто зручно, якщо пакет визначає те, що буде імпортуватися часто, так, як API. Ця закономірність сприяє прихильності до піфонічної філософії "плоскіше, ніж вкладена".

Приклад

Ось приклад одного з моїх проектів, в якому я часто імпортую sessionmakerпокликаний Sessionвзаємодіяти зі своєю базою даних. Я написав пакет "бази даних" з кількома модулями:

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

Мій __init__.pyмістить наступний код:

import os

from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine

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

Оскільки я Sessionтут визначаю , я можу розпочати новий сеанс, використовуючи синтаксис нижче. Цей код буде той самий, що виконується всередині або зовні каталогу каталогу "база даних".

from database import Session
session = Session()

Звичайно, це невелика зручність - альтернативою було б визначити Sessionв новому файлі на зразок "create_session.py" в моєму пакеті баз даних і почати нові сеанси, використовуючи:

from database.create_session import Session
session = Session()

Подальше читання

Тут є досить цікава нитка reddit, що висвітлює відповідне використання __init__.pyтут:

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

Думається, що __init__.pyфайли повинні бути дуже тонкими, щоб уникнути порушення філософії "явне краще, ніж неявне".


3
engine, sessionmaker, create_engine, І osвсе це може також бути імпортовано з database... схоже , як ти наплутав це простір імен.
ArtOfWarfare

9
@ArtOfWarfare, ви можете використовувати __all__ = [...]обмеження того, що отримує з імпорту import *. Але крім цього, так, вам залишається безладний простір імен верхнього рівня.
Натан Гулд

Чи можу я знати, що таке "DATABASE URL"? Я намагався повторити це, додавши create_engine до 'mysql + mysqldb: // root: python @ localhost: 3306 / test', але це не працює. Дякую.
SunnyBoiz

2
Як би ви отримали доступ до класу "Сесія", визначеного в init, зсередини всередині пакета, наприклад, quieries.py?
vldbnc

252

Існує 2 основні причини __init__.py

  1. Для зручності: іншим користувачам не потрібно знати точне розташування ваших функцій у ієрархії ваших пакетів.

    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

    тоді інші можуть викликати додаток ()

    from your_package import add

    не знаючи file1, як

    from your_package.file1 import add
  2. Якщо ви хочете щось ініціалізувати; наприклад, ведення журналу (який слід поставити на верхньому рівні):

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

7
Ой, перш ніж прочитати вашу відповідь, я вважав, що виклик функції явно з її місцезнаходження - це хороша практика.
Ерін

2
@Aerin, краще не вважати короткі твердження (або, у цьому випадку, суб'єктивні висновки) завжди правдивими. Імпорт із них __init__.pyможе бути корисним іноді, але не завжди.
Тобіас Сетте

2
Чи виконуються ці коди під час імпорту чи часу виконання?
користувач1559897

111

Цей __init__.pyфайл змушує Python розглядати каталоги, що містять його як модулі.

Крім того, це перший файл, який завантажується в модуль, тому ви можете використовувати його для виконання коду, який потрібно запустити щоразу, коли модуль завантажується, або вказати підмодулі, які потрібно експортувати.


89

Оскільки Python 3.3, __init__.pyбільше не потрібно визначати каталоги як імпортні пакети Python.

Перевірити PEP 420: Неявні пакети простору імен :

Рідна підтримка каталогів пакетів, які не потребують __init__.py маркерних файлів і можуть автоматично охоплювати декілька сегментів шляху (надихнуті різними підходами сторонніх програм до пакетів простору імен, як описано в PEP 420 )

Ось тест:

$ 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

посилання:
https://docs.python.org/3/whatsnew/3.3.html#pep-420-implicit-namespace-packages
https://www.python.org/dev/peps/pep-0420/
Є __init__. py не потрібен для пакетів в Python 3?


3
Це пакет "простору імен". Не використовуйте його для звичайної упаковки.
метан

@methan, ви могли б детальніше розказати свій коментар?
Роберт Лугг


57

У Python визначення пакету дуже просте. Як і Java, ієрархічна структура та структура каталогів однакові. Але ви повинні мати __init__.pyв пакеті. Я поясню __init__.pyфайл на прикладі нижче:

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__.pyможе бути порожнім, доки він існує. Це вказує, що каталог слід розглядати як пакет. Звичайно,__init__.py також можна встановити відповідний зміст.

Якщо ми додамо функцію в module_n1:

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

Після запуску:

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

function_X in module_n1 

Потім ми слідували за ієрархічним пакетом і називали module_n1 функцією. Ми можемо використовувати __init__.pyв subPackage_b так:

__all__ = ['module_n2', 'module_n3']

Після запуску:

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

Отже, використовуючи імпорт *, пакет модулів залежить від __init__.pyвмісту.


Як буде виглядати мій setup.py той же імпорт через упаковану бібліотеку? from package_x.subPackage_b.module_n1 import function_X
технозі

тому ключовим ключем тут є "використання * імпорту, пакет модулів підлягає вмісту init .py"
Мінні

54

Хоча Python працює без __init__.pyфайлу, ви все одно повинні включати його.

Він визначає, що пакет слід розглядати як модуль, тому включайте його (навіть якщо він порожній).

Також є випадок, коли ви можете фактично використовувати __init__.pyфайл:

Уявіть, у вас була така файлова структура:

main_methods 
    |- methods.py

І methods.pyмістив це:

def foo():
    return 'foo'

Для використання foo()вам знадобиться одне з наступних:

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

Можливо, там вам потрібно (або хочете) зберегти methods.pyвсередині main_methods(наприклад, час виконання / залежність), але вам потрібно лише імпортувати main_methods.


Якщо ви змінили ім'я methods.pyна, __init__.pyви можете використовувати foo()його, просто імпортуючи main_methods:

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

Це працює, тому що __init__.pyрозглядається як частина пакету.


Деякі пакети Python насправді роблять це. Приклад - JSON , де запущений import jsonфактично імпорт __init__.pyіз jsonпакету ( див. Структуру файлу пакета тут ):

Вихідний код: Lib/json/__init__.py


39

__init__.py буде розглядати каталог, в якому він знаходиться, як модуль, що завантажується.

Для людей, які віддають перевагу читання коду, я розміщую тут коментар двобітного алхіміка .

$ 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

Це полегшує імпорт інших файлів python. Коли ви розмістили цей файл у каталозі (скажімо, речі), що містить інші файли py, ви можете зробити щось на зразок імпорту stuff.other.

root\
    stuff\
         other.py

    morestuff\
         another.py

Без цього __init__.pyвсередині каталогу матеріалів ви не можете імпортувати other.py, оскільки Python не знає, де знаходиться вихідний код для цього матеріалу, і не може розпізнати його як пакет.


2
У мене однакова структура в моєму проекті (python 3.4), але я не в змозі зробити Another.py див. Other.py. Як мені зробити імпорт? з root.stuff імпортувати інше? Він працює в режимі налагодження VSCode, але не в командному рядку. Будь-які ідеї?
rodrigorf

10

__init__.pyФайл робить імпорт легко. Якщо __init__.pyв пакет є присутність, функцію a()можна імпортувати з файлу b.pyтак:

from b import a

Однак без нього не можна імпортувати безпосередньо. Ви повинні внести зміни до системного шляху:

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

from b import a

що ви маєте на увазі " функцію a () можна імпортувати з файлу b.py [snippet] Без цього, проте, ви не можете імпортувати безпосередньо. "? Я можу імпортувати функцію a () з файлу b.py без __init__.py.
адерхокс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.