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


Ответы:


1456

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

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

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


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)

7
о, прежде чем прочитать ваш ответ, я подумал, что вызов функции явно из ее местоположения - хорошая практика.
— Аэрин

2
@ Аэрин было бы лучше не считать короткие утверждения (или, в данном случае, субъективные выводы) всегда верными. Импорт из __init__.pyможет быть полезным иногда, но не всегда.
— Тобиас Сетте

2
Этот код выполняется во время импорта или во время выполнения?
— user1559897

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/
Is __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

Следовательно, используя * 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
>>> 

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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.