Импорты
Что такое модуль?
подробнее
Модуль - это файл с расширением
.py.
Зачем нужны модули?
- Разделение логики: Код не лежит одной «простыней», а разбит на тематические файлы (например,
db.py,utils.py). - Повторное использование: Один раз написанный модуль можно подключать в любые другие проекты через
import. - Пространство имен: Функции с одинаковыми именами в разных модулях не конфликтуют (например,
math.sqrtиcmath.sqrt).
Что такое пакет?
подробнее
Пакет - это способ объединить несколько модулей в одну общую структуру (папку). Если модуль - это файл, то пакет - это директория с модулями.
Когда выполняется __init__.py? Как много раз он будет выполнен?
подробнее
Файл
__init__.pyвыполняется ровно один раз при первом импорте пакета (или любого модуля внутри него) в рамках текущего сеанса работы программы.
При повторном вызовеimportв любой части программы Python просто берет уже готовый объект из памяти, не перечитывая файл__init__.py.
Как работает import my_module?
подробнее
- Python ищет файл в списке путей
sys.path. - Превращает
.pyв байт-код.pyc(папка__pycache__). - Код модуля выполняется сверху вниз один раз. Все созданные объекты сохраняются в словаре
sys.modules. - Если импортировать модуль второй раз, Python просто возьмет его из кэша
sys.modules, не выполняя код заново.
В чём разница между import foo и from foo import bar?
подробнее
import foo— импорт «контейнера».
from foo import bar— импорт «содержимого». Копируется ссылка на объект. Если вfooпеременнойbarприсвоят новый объект, то локальная копия продолжит смотреть на старую ссылку.
- Чистота имен:
import foo- безопаснее. Вы точно знаете, чтоbarпришел изfoo, и он не перекроет вашу локальную переменную с тем же именем.from foo import bar- короче, но если у вас уже есть переменнаяbar, будет коллизия.
- Объем загрузки:
- В обоих случаях Python полностью исполняет код модуля foo и загружает его в память (sys.modules).
- Динамическое обновление:
- Если
import fooи внутриfooизменится значениеbar, то увидеть изменение можно черезfoo.bar. - Если
from foo import bar, то создаётся локальная копия ссылки. Если оригинал в модуле переназначат, локальная переменнаяbarвсё равно будет указывать на старый объект.
- Если
Пример динамического обновления:
# foo.py
bar = "Оригинал"
def update():
global bar
bar = "Новое значение" # Переназначаем переменную внутри модуля
# main.py
from foo import bar, update
print(bar) # Выведет: "Оригинал"
update() # Внутри foo.py bar теперь "Новое значение"
print(bar) # Выведет: "Оригинал" (СВЯЗЬ РАЗОРВАНА!)
# main2.py
import foo
print(foo.bar) # Выведет: "Оригинал"
foo.update() # Внутри foo.py bar изменился
print(foo.bar) # Выведет: "Новое значение" (ВСЁ ОКТУАЛЬНО)
В чём разница между from foo import bar и from foo import *?
подробнее
from foo import bar- импортируется только конкретный объектbarfrom foo import *- импортируются все объекты из модуля (кроме тех, что начинаются с_)
Как контролировать что передастся при import *?
Для этого нужно создать список строк
__all__, где каждая строка - это имя (переменной, функции или класса).
# foo.py
__all__ = ['bar'] # При import * импортируется ТОЛЬКО bar
bar = 1
secret = 2 # Это не импортируется через *
Относительные импорты.
подробнее
Относительный импорт - это способ импортировать модули внутри одного пакета, указывая путь «от текущего файла» с помощью точек.
from . import models— из этой же папки.from ..utils import tools— из папки уровнем выше.from .. import config— из корня пакета.
Почему относительные импорты не работают в модуле который запущен напрямую?
У модуля внутри пакета имя выглядит как
package.subpacket.module. Точки в имени помогают Python понять, где «верх», а где «низ».
Python определяет путь импорта на основе переменной__name__. Если модуль запущен напрямую (напримерpython main.py), имя__name__всегда равно"__main__". В этом имени нет точек, поэтому Python «не знает», где находится этот файл относительно других, и не может отсчитать путь вверх или вбок.
Зачем используется as при импорте?
подробнее
Ключевое слово
as(псевдоним) используется для переименования импортируемого модуля или объекта прямо в текущем коде.
-
Борьба с коллизиями (конфликтами)
Если у в коде уже есть функцияopenно нужна еще одна из модуляosfrom os import open as os_open -
Сокращение длинных путей
import tensorflow as tf import pandas as pd -
Улучшение читаемости
from src.utils.data_loaders import load_config as get_cfg -
Унификация кода (Interface matching)
Чтобы код работал с разными библиотеками под одним именем.try: import simplejson as json except ImportError: import json
Когда возникает проблема циклического импорта и как её решить?
подробнее
Циклический импорт возникает, когда два модуля пытаются импортировать друг друга одновременно:
AимпортируетB, аBв это же время импортируетA.
Как решить?
-
Импорт внутри функции (Local Import).
Перенесиimportиз начала файла внутрь функции, где он реально нужен. Модуль загрузится только в момент вызова функции, когда оба файла уже инициализированы.# A.py def do_something(): from B import func_b # Импорт внутри, а не сверху func_b() - Объединение модулей.
ОбъединитьAиBв один модуль. - Вынос общей логики.
Создай третий модульC.py, вынеси туда общие данные/функции, и пусть иA, иBимпортируют их оттуда. - Использование
import fooвместоfrom foo import bar.
Иногда (но не всегда) полные импорты модулей помогают, так как Python успевает создать объект модуля вsys.modulesдо того, как начнет искать в нем конкретные атрибуты.
Что такое if __name__ == "__main__"?
подробнее
if __name__ == "__main__"- это защитный механизм, который разделяет код на библиотечный (функции, классы) и исполняемый (скрипт). А также позволяет использовать один и тот же файл и как самостоятельную программу, и как модуль для других программ без побочных эффектов при импорте.
У каждого модуля в Python есть встроенная переменная __name__. Ее значение зависит от того, как запущен файл:
- Если файл запущен напрямую (через консоль):
__name__всегда равно"__main__". - Если файл импортирован другим модулем:
__name__равно имени файла (модуля).
Пример:
def add(a, b):
return a + b
# Этот блок выполнится ТОЛЬКО если запустить файл напрямую
if __name__ == "__main__":
print("Запуск теста:")
print(add(2, 2))
Что такое from __future__ import?
подробнее
from __future__ import- это механизм обратной совместимости, который позволяет использовать функции из будущих версий Python в текущей.
Когда разработчики Python вводят фичу, которая ломает старый код (например, меняет логику деления или строк), они сначала добавляют её в модуль__future__.Важный нюанс: Эта инструкция обязана быть самой первой строкой в модуле (до любого другого кода или импортов). Потому что это директива компилятору, меняющая правила синтаксиса на этапе разбора (парсинга). Если Python уже начал читать код по старым правилам, применить новые правила на ходу он не сможет — возникнет
SyntaxError.