Я смотрю, как работает модельная система в django, и заметил кое-что, чего не понимаю.
Я знаю, что вы создаете пустой файл __init__.py, чтобы указать, что текущий каталог является пакетом. И что вы можете установить некоторую переменную в __init__.py, чтобы import * работал правильно.
Но django добавляет кучу операторов from ... import ... и определяет кучу классов в __init__.py. Почему? Разве это не просто беспорядочно? Есть ли причина, по которой этот код требуется в __init__.py?
Я не вижу никаких операторов импорта в __init__.py django 1.8. Это было для более старой версии? если да, то какая версия?






Все операции импорта в __init__.py становятся доступными, когда вы импортируете пакет (каталог), который его содержит.
Пример:
./dir/__init__.py:
import something
./test.py:
import dir
# can now use dir.something
Обновлено: забыл упомянуть, что код __init__.py запускается при первом импорте любого модуля из этого каталога. Так что обычно это хорошее место для размещения любого кода инициализации на уровне пакета.
EDIT2: dgrant указал на возможную путаницу в моем примере. В __init__.pyimport 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. Я действительно не рассматриваю код инициализации этих классов (но, возможно, я ошибаюсь в этом).
Вероятно, это классы, которые будут полезны каждый раз, когда вы работаете с пакетом. Но я не хочу строить догадки, может быть много причин, по которым они существуют, объективные или нет :)
Это также может быть по историческим причинам. Когда вы конвертируете модуль в пакет, module.py в module / __ init__.py весь существующий код может использовать его, как и раньше, но теперь модуль может иметь подмодули.
Модули неявно выполняют родительский __init__.py. Импортируя модули внутри __init__.py, вы создаете циклический импорт. __init__.py не будет выполняться полностью перед одним таким импортом. Безопаснее оставлять __init__.py пустым.
Важно отметить, что это не относится к файлам __init__.py. Если бы у вас был файл dir/other.py, в котором было что-то вроде from datetime import datetime, вы также могли бы вызвать dir.other.datetime.now() или даже from dir.other import datetime.
На самом деле это просто личные предпочтения, и они связаны с компоновкой ваших модулей 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 / модели / поля. Я предполагаю, что они сделали это, потому что не хотели втиснуть все в гипотетическую модель django / БД / модели / fields.py, поэтому они разделили ее на несколько подмодулей (например, related.py, files.py) и вставили созданные * определения полей в сам модуль полей. (следовательно, __init__.py).
dgrant, я имел в виду, что something может быть внешним модулем, dir.something будет работать. Спасибо за комментарий, отредактирую свой пост, чтобы было понятнее.
Использование файла __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: Дело в том, что это не часть публичного API. Если вы переименуете модуль, вы можете быть уверены, что ни один зависимый код не сломается. Кроме того, это гарантирует, что элементы API появляются только один раз, например, когда самоанализ используется для автоматического создания документации API.
@Arlen: Удаление модуля предотвращает прямой доступ к import <pack>.somemodule1. Вы можете импортировать только объекты <pack>, определенные или импортированные в его __init__.py, и не удаленные подмодули.
Дело не в Django, не так ли? Да, вы впервые увидели это в Django, но это больше похоже на чистый Python - возможно, тег Django действительно не подходит.