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

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

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

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

Дело не в Django, не так ли? Да, вы впервые увидели это в Django, но это больше похоже на чистый Python - возможно, тег Django действительно не подходит.

S.Lott 23.09.2008 16:46

Я не вижу никаких операторов импорта в __init__.py django 1.8. Это было для более старой версии? если да, то какая версия?

Gobi Dasu 03.01.2017 09:51
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
85
2
64 320
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Все операции импорта в __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. Я действительно не рассматриваю код инициализации этих классов (но, возможно, я ошибаюсь в этом).

Erik 23.09.2008 08:57

Вероятно, это классы, которые будут полезны каждый раз, когда вы работаете с пакетом. Но я не хочу строить догадки, может быть много причин, по которым они существуют, объективные или нет :)

Alexander Kojevnikov 23.09.2008 09:08

Это также может быть по историческим причинам. Когда вы конвертируете модуль в пакет, module.py в module / __ init__.py весь существующий код может использовать его, как и раньше, но теперь модуль может иметь подмодули.

Łukasz 24.09.2008 10:33

Модули неявно выполняют родительский __init__.py. Импортируя модули внутри __init__.py, вы создаете циклический импорт. __init__.py не будет выполняться полностью перед одним таким импортом. Безопаснее оставлять __init__.py пустым.

Ivo Danihelka 09.07.2010 13:24

Важно отметить, что это не относится к файлам __init__.py. Если бы у вас был файл dir/other.py, в котором было что-то вроде from datetime import datetime, вы также могли бы вызвать dir.other.datetime.now() или даже from dir.other import datetime.

Carles Sala 05.07.2016 16:49

На самом деле это просто личные предпочтения, и они связаны с компоновкой ваших модулей 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 будет работать. Спасибо за комментарий, отредактирую свой пост, чтобы было понятнее.

Alexander Kojevnikov 23.09.2008 10:58

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

nikow 18.08.2011 14:33

@Arlen: Удаление модуля предотвращает прямой доступ к import <pack>.somemodule1. Вы можете импортировать только объекты <pack>, определенные или импортированные в его __init__.py, и не удаленные подмодули.

MestreLion 13.04.2012 23:12

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

Синтаксис инициализации коллекции в Visual Basic 2008?
Инициализация массива по произвольному начальному индексу в с #
Различия между слияниями наборов данных
Инициализирует ли инициализация значения сначала нулевые значения членов объекта даже с предоставленным инициализатором элемента по умолчанию в C++?
Может ли кто-нибудь объяснить правила времени жизни объектов и неинициализированной памяти в C++ (контекст: `std::inplace_vector`)?
Различные примечания для неинициализированного члена внутри общей лямбды в GCC 9 и GCC 10
Почему неявно сгенерированный конструктор отличается от конструктора, предоставленного пользователем?
Использование ранее установленных полей при инициализации структуры составными литералами
JavaFX не инициализирует представления
Есть ли выигрыш в производительности от использования алгоритмов c++ std(::ranges)::uninitialized_... и стоит ли не использовать constexpr?