Для чего нужен __init__.py?

Ответы:

1456

Раньше он был обязательной частью пакета ( старый, до 3.3 «обычный пакет» , а не более новый 3.3+ «пакет пространства имен» ).

Вот документация.

Python определяет два типа пакетов, обычные пакеты и пакеты пространства имен. Обычные пакеты - это традиционные пакеты, которые существовали в Python 3.2 и более ранних версиях. Обычный пакет обычно реализуется как каталог, содержащий __init__.pyфайл. Когда обычный пакет импортируется, этот __init__.pyфайл выполняется неявно, и определяемые им объекты привязываются к именам в пространстве имен пакета. __init__.pyФайл может содержать один и тот же код Python , что любой другой модуль может содержать, и Python будет добавить некоторые дополнительные атрибуты модуля , когда он импортируется.

Но просто нажмите на ссылку, она содержит пример, дополнительную информацию и объяснение пакетов пространства имен, типа пакетов без __init__.py.

Loki
источник
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 (потому что он импортирует ваш пустой файл сейчас). Если вы повторите эти шаги без создания пустого файла инициализации, этого не произойдет. Это то, что он призван предотвратить.
Двухразрядный алхимик
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 ).
Мэггеро
846

Названные __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

основываясь на этом

caritos
источник
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
Не используйте это. Это пакет "namespace", а не обычный пакет. Пакет namespace используется в очень редких случаях. Возможно, вам не нужно знать, когда его использовать. Просто используйте __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

My __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здесь, я могу начать новый сеанс, используя синтаксис ниже. Этот код будет одинаково выполняться изнутри или снаружи каталога пакета «database».

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 *. Но кроме этого, да, у вас осталось грязное пространство имен верхнего уровня.
Натан Гулд
Могу ли я узнать, что такое «URL базы данных»? Я попытался повторить это, заключив create_engine в «mysql + mysqldb: // root: python @ localhost: 3306 / test», но это не работает. Спасибо.
SunnyBoiz
2
Как бы вы получили доступ к классу 'Session', определенному в init, изнутри пакета, например, quieries.py?
vldbnc
253

Есть 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

    тогда другие могут вызвать add ()

    from your_package import add

    не зная file1, вроде

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

    import logging.config
    logging.config.dictConfig(Your_logging_config)
flycee
источник
7
о, прежде чем прочитать ваш ответ, я подумал, что вызов функции явно из ее местоположения - хорошая практика.
Аэрин
2
@ Аэрин было бы лучше не считать короткие утверждения (или, в данном случае, субъективные выводы) всегда верными. Импорт из __init__.pyможет быть полезным иногда, но не всегда.
Тобиас Сетте
2
Этот код выполняется во время импорта или во время выполнения?
user1559897
111

__init__.pyФайл делает Python лечить каталоги , содержащие его в качестве модулей.

Кроме того, это первый файл, который будет загружен в модуль, поэтому вы можете использовать его для выполнения кода, который вы хотите запускать каждый раз при загрузке модуля, или указывать субмодули для экспорта.

Can Berk Güder
источник
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/
Is __init__. py не требуется для пакетов в Python 3?

zeekvfu
источник
3
Это пакет "пространство имен". Не используйте его для обычной упаковки.
метан
@methan, не могли бы вы уточнить ваш комментарий?
Роберт
3
@RobertLugg См. Dev.to/methane/don-t-omit-init-py-3hga
метан
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

Следовательно, используя * import, модуль пакета зависит от __init__.pyсодержимого.

Маркус Торнтон
источник
Как будет выглядеть мой setup.py для того же импорта через упакованную библиотеку? from package_x.subPackage_b.module_n1 import function_X
Technazi
поэтому ключевой момент здесь - это «использование * импорта, пакет модуля зависит от содержимого 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
>>> 
B.Mr.W.
источник
30

Это облегчает импорт других файлов Python. Когда вы помещаете этот файл в каталог (скажем, вещи), содержащий другие файлы py, вы можете сделать что-то вроде import stuff.other.

root\
    stuff\
         other.py

    morestuff\
         another.py

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

эпитафия
источник
2
У меня есть та же структура в моем проекте (Python 3.4), но я не могу заставить другой файл увидеть другой файл. Как мне сделать импорт? из root.stuff импортировать другие? Он работает в режиме отладки VSCode, но не в командной строке. Любые идеи?
Родригор
10

__init__.pyФайл делает импорт легко. Когда __init__.pyв пакете присутствует an , функцию 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.
aderchox