Вопрос по initialization, python, package – Добавление кода в __init__.py

81

Я смотрю на то, как работает модельная система в django, и заметил кое-что, чего не понимаю.

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

Но django добавляет несколько операторов from ... import ... и определяет несколько классов в__init__.py, Зачем? Разве это не делает вещи беспорядочными? Есть ли причина, по которой этот код требуется в__init__.py?

Это не правда о Джанго? Да, вы впервые увидели это в Django, но это больше похоже на чистый Python - возможно, тег Django не совсем уместен. S.Lott
Я не вижу никаких отчетов об импорте в__init__.py Джанго 1.8. Это было для старой версии? если да, то какая версия? Gobi Dasu

Ваш Ответ

3   ответа
69

__init__.py становятся доступными при импорте пакета (каталога), в котором он находится.

Пример:

./dir/__init__.py:

import something

./test.py:

import dir
# can now use dir.something

РЕДАКТИРОВАТЬ: забыл упомянуть, код в__init__.py запускается при первом импорте любого модуля из этого каталога. Так что обычно это хорошее место для размещения любого кода инициализации на уровне пакета.

EDIT2: dgrant указал на возможную путаницу в моем примере. В__init__.py import something Можно импортировать любой модуль, не обязательно из пакета. Например, мы можем заменить его наimport datetimeто в нашем верхнем уровнеtest.py оба эти фрагмента будут работать:

import dir
print dir.datetime.datetime.now()

а также

import dir.some_module_in_dir
print dir.datetime.datetime.now()

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

Хорошо спасибо. Но я все еще не уверен, почему было бы неплохо добавить классы в__init__.py  Я действительно не рассматриваю код инициализации этих классов (но, возможно, я ошибаюсь по этому поводу). Erik
Важно отметить, что это не является чем-то конкретным для__init__.py файлы. Если у вас был файлdir/other.py что-то вродеfrom datetime import datetime Вы также сможете позвонитьdir.other.datetime.now() или дажеfrom dir.other import datetime.
Это также может быть по историческим причинам. Когда вы конвертируете модуль в пакет, module.py в module / __ init__.py весь существующий код может использовать его, как и раньше, но теперь модуль может иметь подмодули.
Модули исполняют родительские__init__.py неявно. Импортируя модули внутри__init__.pyВы создаете циклический импорт.__init__.py не будет выполнен полностью до одного такого импорта. Безопаснее хранить__init__.py пустой.
Вероятно, это те классы, которые полезны каждый раз, когда вы работаете с пакетом. Но я не хочу спекулировать, может быть много причин, почему они есть, объективные или нет :)
34

аших модулей Python.

Допустим, у вас есть модуль с именемerikutils, Есть два способа, которыми он может быть модулем, либо у вас есть файл с именемerikutils.py на вашеsys.path или у вас есть каталог с именемerikutils на вашеsys.path с пустым__init__.py файл внутри него. Тогда, скажем, у вас есть группа модулей под названиемfileutils, procutils, parseutils и вы хотите, чтобы они были субмодулями подerikutils, Таким образом, вы делаете несколько файлов .py под названиемfileutils.py, procutils.py, а такжеparseutils.py:

erikutils
  __init__.py
  fileutils.py
  procutils.py
  parseutils.py

Может быть, у вас есть несколько функций, которые просто не принадлежатfileutils, procutils, или жеparseutils модули. И скажем, вам не хочется создавать новый модуль под названиемmiscutils, AND, вы хотели бы иметь возможность вызывать функцию следующим образом:

erikutils.foo()
erikutils.bar()

вместо того, чтобы делать

erikutils.miscutils.foo()
erikutils.miscutils.bar()

Так какerikutils модуль - это каталог, а не файл, мы должны определить его функции внутри__init__.py файл.

В Django лучший пример, который я могу придумать,django.db.models.fields, ВСЕ классы django * Field определены в__init__.py файл вdjango/db/models/fields каталог. Я думаю, что они сделали это, потому что они не хотели втиснуть все в гипотетическийdjango/db/models/fields.py модели, чтобы они разбили его на несколько подмодулей (related.py, files.py, например), и они приклеили сделанные определения * Field в самом модуле fields (следовательно,__init__.py).

dgrant, я имел в виду, чтоsomething может быть внешний модуль, подоконник будет работать. Спасибо за комментарий, я отредактирую свой пост, чтобы сделать его более понятным.
28

__init__.py Файл позволяет сделать внутреннюю структуру пакета невидимой снаружи. Если внутренняя структура изменяется (например, из-за того, что вы разбили один жирный модуль на два), вам нужно только настроить__init__.py файл, но не код, который зависит от пакета. Вы также можете сделать части вашего пакета невидимыми, например, если они не готовы к общему использованию.

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

from somemodule import some_function1, some_function2, SomeObject

del somemodule

Теперь, если вы решили разделитьsomemodule новый__init__.py возможно:

from somemodule1 import some_function1, some_function2
from somemodule2 import SomeObject

del somemodule1
del somemodule2

Снаружи упаковка все еще выглядит точно так же, как и раньше.

какой смысл удалять модуль?
@Arlen: удаление модуля предотвращаетimport <pack>.somemodule1 непосредственно. Вы можете импортировать только из<pack> объекты, определенные или импортированные в его__init__.pyи не удаленные подмодули.
@Arlen: Дело в том, что он не является частью публичного API. Если вы переименуете модуль, вы можете быть уверены, что никакой зависимый код не нарушается. Кроме того, это гарантирует, что элементы API появляются только один раз, например, когда самоанализ используется для автоматического создания документации API.

Похожие вопросы