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

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

81 голосов
/ 23 сентября 2008

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

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

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

Ответы [ 3 ]

69 голосов
/ 23 сентября 2008

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

Пример:

./dir/__init__.py

import something

./test.py:

import dir
# can now use dir.something

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

РЕДАКТИРОВАТЬ2: Драгант указал на возможную путаницу в моем примере. В __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, будь то импортированные модули, функции или классы, автоматически доступны в пространстве имен пакета при каждом импорте пакета или модуля в пакете.

34 голосов
/ 23 сентября 2008

Это действительно личное предпочтение, и оно связано с компоновкой ваших модулей 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 * определены в файле __init__.py в каталоге django / db / models / fields . Я думаю, что они сделали это, потому что они не хотели втиснуть все в гипотетическую django / db / models / fields.py модель, поэтому они разбили ее на несколько подмодулей ( related.py , files.py , например), и они прикрепили сделанные * определения полей в самом модуле полей (следовательно, __init__.py).

28 голосов
/ 24 сентября 2008

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

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

...