Добавление кода в __init__.py

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

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

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

Ответ 1

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

Пример:

./dir/__init__.py:

import something

./test.py:

import dir
# can now use dir.something

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

Ответ 2

Это просто личное предпочтение, и оно связано с расположением ваших модулей 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. И, вы хотели бы иметь возможность вызвать функцию следующим образом:

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) и они закрепили сделанные * определения полей в самом модуле полей (следовательно, __init__.py).

Ответ 3

Использование файла __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

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