Добавление кода в __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
С внешней стороны пакет выглядит так же, как и раньше.