Додавання коду до __init__.py


85

Я розглядаю, як працює модель моделі в django, і помітив щось, чого не розумію.

Я знаю, що ви створюєте порожній __init__.pyфайл, щоб вказати, що поточний каталог - це пакет. І що ви можете встановити деяку змінну, __init__.pyщоб імпорт * працював належним чином.

Але django додає купу операторів from ... import ... та визначає купу класів у __init__.py. Чому? Хіба це не просто робить речі безладними? Чи є причина, яка вимагає використання цього коду __init__.py?


13
Це насправді не про Django, чи не так? Так, ви бачили це спочатку в Django, але це здається більше схожим на чистий Python - можливо, тег Django насправді не підходить.
S.Lott,

Я не бачу заяв на імпорт у __init__.pydjango 1.8. Це було для старшої версії? якщо так, то яку версію?
Гобі Дасу

Відповіді:


72

Весь імпорт у __init__.pyдоступний, коли ви імпортуєте пакет (каталог), що його містить.

Приклад:

./dir/__init__.py:

import something

./test.py:

import dir
# can now use dir.something

EDIT: забув згадати, код in __init__.pyзапускається під час першого імпорту будь-якого модуля з цього каталогу. Тому зазвичай це гарне місце для розміщення будь-якого коду ініціалізації на рівні пакета.

EDIT2: dgrant вказав на можливу плутанину в моєму прикладі. In __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, будь то імпортовані модулі, функції або класи, автоматично доступні у просторі імен пакунків щоразу, коли ви імпортуєте пакет або модуль у пакеті.


Добре, дякую. Але я все ще не впевнений, чому було б непогано додавати класи до того, що __init__.py я насправді не розглядаю код ініціалізації цих класів (але, можливо, я помиляюся).
Ерік,

Це, мабуть, ті класи, які корисні щоразу, коли ви працюєте з пакетом. Але я не хочу міркувати, може бути багато причин, чому вони там, об’єктивні чи ні :)
Олександр Коєжников

13
Це також може бути з історичних причин. Коли ви перетворюєте модуль в пакет, module.py в module / __ init__.py весь існуючий код може використовувати його, як і раніше, але тепер модуль може мати підмодулі.
kasukasz

1
Модулі виконують батьківські __init__.pyнеявно. Імпортуючи модулі всередині __init__.py, ви створюєте циклічний імпорт. __init__.pyЧи не буде виконана повністю , перш ніж одного такого імпорту. Безпечніше залишатись __init__.pyпорожнім.
Ivo Danihelka

Важливо зауважити, що це не є нічим специфічним для __init__.pyфайлів. Якби у вас був файл, в dir/other.pyякому було щось подібне, from datetime import datetimeви б також могли зателефонувати dir.other.datetime.now()або навіть from dir.other import datetime.
Carles Sala

37

Це насправді лише особисті переваги, і це пов’язано з розташуванням ваших модулів 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 . Я припускаю , що вони зробили це , тому що вони не хочуть , щоб втиснути все в гіпотетичній Джанго / дб / моделі / fields.py моделі, тому вони розділили його на кілька подмодулей ( related.py , files.py , наприклад) і вони закріпили зроблені * визначення полів у самому модулі поля (отже, __init__.py).


1
dgrant, я мав на увазі, що це somethingможе бути зовнішнім модулем, dir. Дякую за коментар, я відредагую свою публікацію, щоб зробити її більш зрозумілою.
Олександр Кожєвніков

29

Використання __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

Зовні упаковка все ще виглядає точно так само, як і раніше.


1
@Arlen: Справа в тому, що він не є частиною загальнодоступного API. Якщо ви перейменуєте модуль, ви можете бути впевнені, що жоден залежний код не зламається. Крім того, це гарантує, що елементи API відображаються лише один раз, наприклад, коли самоаналіз використовується для автоматичного створення документації API.
nikow

5
@Arlen: Видалення модуля заважає import <pack>.somemodule1безпосередньо модулю . Імпортувати можна лише з <pack>об’єктів, визначених або імпортованих у його __init__.py, та не видалених підмодулів.
MestreLion
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.