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

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

15 ответов

Решение

Это часть пакета. Вот документация.

__init__.py файлы необходимы для того, чтобы Python рассматривал каталоги как содержащие пакеты; это сделано для предотвращения каталогов с общим именем, таких как stringот непреднамеренного сокрытия действительных модулей, которые встречаются позже (глубже) в пути поиска модулей. В простейшем случае __init__.py может быть просто пустым файлом, но он также может выполнить код инициализации для пакета или установить __all__ переменная, описанная позже.

Файлы с именем __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 Файл обычно пуст, но его можно использовать для экспорта выбранных частей пакета под более удобным именем, хранения вспомогательных функций и т. д. Учитывая приведенный выше пример, доступ к содержимому модуля init можно получить как

import spam

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

В дополнение к маркировке каталога как пакета 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 Файлы должны быть очень тонкими, чтобы не нарушать философию "явный лучше, чем неявный".

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

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

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

Начиная с 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
https://www.python.org/dev/peps/pep-0420/
Не требуется ли __init__.py для пакетов в Python 3?

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

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

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

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

root\
    stuff\
         other.py

    morestuff\
         another.py

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

Для чего используется __init__.py?

Основное использование __init__.py это инициализировать пакеты Python. Самый простой способ продемонстрировать это - взглянуть на структуру стандартного модуля Python.

package/
    __init__.py
    file.py
    file2.py
    file3.py
    subpackage/
        __init__.py
        submodule1.py
        submodule2.py

Как вы можете видеть в структуре выше, включение __init__.py Файл в каталоге указывает интерпретатору Python, что каталог должен рассматриваться как пакет Python

Что входит в __init__.py ?

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

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

В приведенном выше примере мы можем сказать, что file.py имеет файл классов. Так что без всего в нашем __init__.py вы бы импортировали с этим синтаксисом:

from package.file import File

Однако вы можете импортировать файл в свой __init__.py чтобы сделать его доступным на уровне пакета:

# in your __init__.py
from file import File

# now import File from package
from package import File

Еще одна вещь, которую нужно сделать, - на уровне пакета сделать подпакеты / модули доступными с __all__ переменная. Когда интерпретатор видит __all__ переменная, определенная в __init__.py он импортирует модули, перечисленные в __all__ переменная, когда вы делаете:

from package import *

__all__ это список, содержащий имена модулей, которые вы хотите импортировать с помощью import *, поэтому, если мы захотим импортировать субмодули в подпакет, снова посмотрим на наш пример выше __all__ переменная в subpackage/__init__.py было бы:

__all__ = ['submodule1', 'submodule2']

С __all__ переменная заполняется так, когда вы выполняете

from subpackage import *

это будет импортировать submodule1 и submodule2.

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

Ссылка

__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

Одна вещь, которую позволяет __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

и в __init__.py сделайте

from util import *

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

from util import foo
from util.db import check_schema

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

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

Если вы используете Python 2 и хотите загрузить одноуровневые файлы вашего файла, вы можете просто добавить родительскую папку вашего файла в системные пути сеанса. Он будет вести себя примерно так же, как если бы ваш текущий файл был файлом инициализации.

      import os
import sys
dir_path = os.path.dirname(__file__)
sys.path.insert(0, dir_path)

После этого обычный импорт относительно каталога файла будет работать нормально. Например

      import cheese
from vehicle_parts import *
# etc.

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

init.py : это файл python, который находится в каталоге пакета, он вызывается при импорте пакета или модуля в пакете. Вы можете использовать это для выполнения кода инициализации пакета, т.е. всякий раз, когда пакет импортируется, операторы python выполняются первыми, прежде чем будут выполнены другие модули в этой папке. Это похоже на основную функцию программы c или java, но она существует в модуле (папке) пакета python, а не в основном файле python. также он имеет доступ к глобальным переменным, определенным в этом файле инициализации .py, как при импорте модуля в файл python.

для например. У меня есть файл init .py в папке с именем pymodlib, этот файл содержит следующие операторы:

print(f'Вызов init.py для { имя}')pystructures = ['for_loop', 'while__loop', 'ifCondition']

когда я импортирую этот пакет «pymodlib» в мой модуль решения, записную книжку или консоль Python: эти два оператора выполняются при импорте. Таким образом, в журнале или консоли вы увидите следующий вывод:

import pymodlib Вызов init.py для pymodlib

в следующем операторе консоли python: я могу получить доступ к глобальной переменной:

pymodlib.pystructures дает следующий результат:

['for_loop', 'while__loop', 'ifCondition']

Теперь, начиная с python3.3, использование этого файла было необязательным, чтобы сделать папку модулем python. Таким образом, вы не включаете его в папку модуля Python.

Другие вопросы по тегам