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

Что означает __init__.py в исходном каталоге Python?


person Mat    schedule 15.01.2009    source источник
comment
Согласно приведенному ниже комментарию @Rob_before_edits и этот поток stackoverflow 37139786, кажется, что init .py не больше необходимо для Python 3.3+.   -  person Jun711    schedule 16.05.2018
comment
Пакет без __init__ - это пакет пространства имен, а не обычный пакет. Это не то же самое, на что указывает @methane с помощью пример здесь.   -  person Catbuilts    schedule 24.01.2021


Ответы (12)


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

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

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

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

person Loki    schedule 15.01.2009
comment
Что это означает: это сделано для того, чтобы каталоги с общим именем, таким как строка, случайно не скрывали допустимые модули, которые появляются позже на пути поиска модулей? - person Carl G; 25.01.2014
comment
@CarlG Python выполняет поиск в списке каталогов для разрешения имен, например, в операторах импорта. Поскольку это может быть любой каталог, а конечный пользователь может добавлять произвольные, разработчикам приходится беспокоиться о каталогах, которые имеют общее имя с допустимым модулем Python, например, «строка» в примере с документами. Чтобы облегчить это, он игнорирует каталоги, которые не содержат файл с именем _ _ init _ _.py (без пробелов), даже если он пуст. - person Two-Bit Alchemist; 08.03.2014
comment
@CarlG Попробуй. Создайте каталог с именем datetime и создайте в нем два пустых файла: файл init.py (с подчеркиванием) и datetime.py. Теперь откройте интерпретатор, импортируйте sys и введите sys.path.insert(0, '/path/to/datetime'), заменив этот путь на путь к любому каталогу, который вы только что создали. Теперь попробуйте что-нибудь вроде from datetime import datetime;datetime.now(). Вы должны получить AttributeError (потому что сейчас он импортирует ваш пустой файл). Если бы вы повторили эти шаги, не создав пустой файл инициализации, этого бы не произошло. Это то, что он предназначен для предотвращения. - person Two-Bit Alchemist; 08.03.2014

Файлы с именами __init__.py используются для пометки каталогов на диске как каталогов пакетов Python. Если у вас есть файлы

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

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

import spam.module

or

from spam import module

Если вы удалите файл __init__.py, Python больше не будет искать подмодули внутри этого каталога, поэтому попытки импортировать модуль не удастся.

Файл __init__.py обычно пуст, но может использоваться для экспорта выбранных частей пакета под более удобным именем, хранения вспомогательных функций и т. Д. В приведенном выше примере доступ к содержимому модуля инициализации можно получить как

import spam

на основе это

person caritos    schedule 07.11.2010
comment
Обновление: файл __init__.py требовался под Python 2.X и по-прежнему требуется под Python 2.7.12 (я тестировал его), но он больше не требуется от (предположительно) Python 3.3 и далее и не требуется под Python 3.4.3 ( Тестировал). Подробнее см. stackoverflow.com/questions/37139786. - person Rob_before_edits; 30.10.2016

Помимо обозначения каталога как пакета 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_white_include_include_include_include_your_opinion_on_on_on_cons_your_opinion_on_in >

Большинство считает, что __init__.py файлы должны быть очень тонкими, чтобы не нарушать философию «явное лучше, чем неявное».

person Nathan Gould    schedule 24.09.2013
comment
engine, sessionmaker, create_engine и os теперь могут быть импортированы из database ... похоже, вы испортили это пространство имен. - person ArtOfWarfare; 23.09.2015
comment
@ArtOfWarfare, вы можете использовать __all__ = [...], чтобы ограничить то, что импортируется с import *. Но помимо этого, да, у вас остается запутанное пространство имен верхнего уровня. - person Nathan Gould; 24.09.2015
comment
@NathanG: Вы также можете использовать одинарные ведущие переменные подчеркивания, которые не импортируются import * по умолчанию. Например: import os as _os и используйте _os внутри модуля __init__.py вместо os. - person Mathieu Rollet; 23.07.2021

Есть 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)
    
person flycee    schedule 08.04.2015
comment
о, прежде чем прочитать ваш ответ, я подумал, что вызов функции явно из ее местоположения - это хорошая практика. - person Aerin; 26.02.2018
comment
@Aerin, было бы лучше не считать короткие утверждения (или, в данном случае, субъективные выводы) верными. Импорт из __init__.py может быть полезен иногда, но не всегда. - person Tobias Sette; 12.12.2019
comment
что должно быть внутри init .py? - person Maryam Pashmi; 18.05.2021

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

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

person Can Berk Güder    schedule 15.01.2009
comment
Я думаю, что init .py заставляет Python рассматривать каталоги как пакеты, а не как модули. См. docs.python.org/3/tutorial/modules.html. - person Moses; 22.07.2020
comment
все пакеты являются модулями, но не все модули являются пакетами - странно, но это правда. - person JacKeown; 13.10.2020

Начиная с 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?

person zeekvfu    schedule 12.10.2016
comment
Это пакет пространства имен. Не используйте его для обычной упаковки. - person methane; 06.04.2019
comment
@methan, не могли бы вы уточнить свой комментарий? - person Robert Lugg; 04.10.2019
comment
@RobertLugg См. dev.to/methane/don-t-omit-init -py-3hga - person methane; 07.10.2019
comment
Как бы то ни было, этот комментарий решил проблему, отнимавшую 4 часа моего рабочего дня. По какой-то причине сегодня утром Spyder решил, что больше не хочет импортировать мой модуль. Прочитав этот комментарий, я избавился от файла init .py, и импорт начал работать. Спасибо @zeekvfu! - person autonopy; 29.06.2020

Хотя 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

person Simon    schedule 12.05.2018

В 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 содержимого.

person Marcus Thornton    schedule 09.01.2014
comment
Как будет выглядеть мой файл setup.py при таком же импорте через упакованную библиотеку? from package_x.subPackage_b.module_n1 import function_X - person technazi; 04.09.2019
comment
поэтому ключевым моментом здесь является использование * импорта, пакет модуля зависит от содержимого init .py - person soMuchToLearnAndShare; 06.03.2020

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

Для людей, которые предпочитают читать код, я помещаю здесь комментарий 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
>>> 
person B.Mr.W.    schedule 03.01.2015

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

root\
    stuff\
         other.py

    morestuff\
         another.py

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

person Epitaph    schedule 15.01.2009
comment
У меня такая же структура в моем проекте (python 3.4), но я не могу заставить another.py видеть other.py. Как мне сделать импорт? из root.stuff импорт другой? Он работает в режиме отладки VSCode, но не в командной строке. Любые идеи? - person rodrigorf; 23.10.2018

Файл __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
person Alec    schedule 25.05.2019

Одна вещь, которую позволяет __init__.py, - это преобразование модуля в пакет без нарушения API или создания посторонних вложенных пространств имен или частных модулей *. Это помогает, когда я хочу расширить пространство имен.

Если у меня есть файл util.py, содержащий

def foo():
    ...

тогда пользователи получат доступ к foo с

from util import foo

Если я затем захочу добавить служебные функции для взаимодействия с базой данных, и я хочу, чтобы у них было собственное пространство имен в util, мне понадобится новый каталог **, и чтобы поддерживать совместимость API (чтобы from util import foo все еще работал), я назовите это util /. Я мог переместить util.py в util / вот так,

util/
  __init__.py
  util.py
  db.py

и в util / __ init__.py выполните

from util import *

но это избыточно. Вместо файла util / util.py мы можем просто поместить содержимое util.py в __init__.py, и теперь пользователь может

from util import foo
from util.db import check_schema

Я думаю, это прекрасно подчеркивает, как __init__.py пакета util действует аналогично модулю util.

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

person joel    schedule 19.07.2020
comment
Разве вы не имеете в виду from util import check_schema, поскольку вы уже сделали это в __init __.py from util import * - person Mark; 16.10.2020
comment
@Mark no, from util import * будет в util / __ init__.py, поэтому не будет импортировать db, а будет импортировать содержимое util / util.py. Уточню ответ - person joel; 16.10.2020