В Python каждый файл с расширением .py является модулем. Модули позволяют разделять код на логические части, переиспользовать функции и классы между проектами и управлять пространством имен. Система импортов -- одна из фундаментальных частей языка.
Что такое модуль
Модуль -- это файл Python, содержащий определения функций, классов и переменных. При импорте модуля Python выполняет весь код в файле один раз и кэширует результат.
# math_utils.py — a simple module
def add(a: float, b: float) -> float:
"""Return the sum of two numbers."""
return a + b
def multiply(a: float, b: float) -> float:
"""Return the product of two numbers."""
return a * b
PI = 3.14159265358979
Инструкция import
Базовый способ подключения модуля -- инструкция import. Она загружает модуль целиком и создает ссылку на него в текущем пространстве имен.
import math_utils
result = math_utils.add(2, 3) # 5
pi = math_utils.PI # 3.14159...
Импорт нескольких модулей
Каждый модуль рекомендуется импортировать отдельной строкой (PEP 8):
# Good practice — one import per line
import os
import sys
import json
# Acceptable for closely related modules
from os import path, getcwd
Псевдонимы (as)
Оператор as позволяет задать короткое имя для модуля:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# Standard aliases in the Python ecosystem
data = np.array([1, 2, 3])
Инструкция from ... import
Позволяет импортировать конкретные имена из модуля напрямую:
from math_utils import add, PI
result = add(2, 3) # No prefix needed
print(PI) # 3.14159...
Импорт всех имен (*)
Импорт через * подтягивает все публичные имена модуля. Это считается плохой практикой для production-кода, так как засоряет пространство имен:
# Avoid in production code
from math_utils import *
# You don't know where 'add' came from when reading code
result = add(2, 3)
Атрибут __name__
Каждый модуль имеет встроенный атрибут __name__. Когда модуль запускается напрямую, __name__ равен "__main__". Когда модуль импортируется, __name__ содержит имя модуля.
# greet.py
def greet(name: str) -> str:
"""Return a greeting message."""
return f"Hello, {name}!"
# This block runs ONLY when the file is executed directly
if __name__ == "__main__":
# Will NOT run when imported as a module
message = greet("World")
print(message)
Это стандартный паттерн, позволяющий использовать файл и как модуль, и как скрипт:
python greet.py # Prints "Hello, World!"
import greet # Does NOT print anything
greet.greet("Python") # "Hello, Python!"
Атрибут __all__
Переменная __all__ -- это список строк, определяющий, какие имена экспортируются при from module import *:
# validators.py
__all__ = ["validate_email", "validate_phone"]
def validate_email(email: str) -> bool:
"""Check if email format is valid."""
return "@" in email and "." in email
def validate_phone(phone: str) -> bool:
"""Check if phone format is valid."""
return phone.startswith("+") and len(phone) >= 10
def _normalize_input(value: str) -> str:
"""Internal helper — not exported."""
return value.strip().lower()
from validators import *
validate_email("[email protected]") # Available
_normalize_input("test") # NOT available (not in __all__)
Путь поиска модулей (sys.path)
Python ищет модули в определенном порядке. Список директорий для поиска хранится в sys.path:
import sys
for path in sys.path:
print(path)
Порядок поиска:
- Директория запускаемого скрипта (или текущая директория)
- Переменная окружения
PYTHONPATH - Директории стандартной библиотеки
- Директория
site-packages(установленные пакеты)
Динамическое добавление путей
import sys
# Add a custom directory to the search path
sys.path.insert(0, "/path/to/my/modules")
# Now Python will also search in this directory
import my_custom_module
Относительные и абсолютные импорты
В пакетах (директориях с __init__.py) можно использовать относительные импорты:
mypackage/
__init__.py
module_a.py
module_b.py
subpackage/
__init__.py
module_c.py
# module_c.py — relative imports within a package
from . import helpers # From current package
from .. import module_a # From parent package
from ..module_b import some_func # Specific name from parent
Абсолютные импорты -- предпочтительный стиль (PEP 8):
# Absolute import — always works, always clear
from mypackage.subpackage import module_c
from mypackage.module_a import MyClass
Кэширование модулей
Python кэширует импортированные модули в словаре sys.modules. Повторный import не перезагружает модуль:
import sys
import json
# Module is cached after first import
print("json" in sys.modules) # True
# Force reload (useful in development/REPL)
import importlib
importlib.reload(json)
Встроенные модули Python
Python поставляется с богатой стандартной библиотекой:
import os # Operating system interface
import sys # System-specific parameters
import json # JSON encoder/decoder
import pathlib # Object-oriented filesystem paths
import datetime # Date and time handling
import collections # Specialized container types
import itertools # Iterator building blocks
import functools # Higher-order functions
import typing # Type hint support
import dataclasses # Data class decorator
Порядок импортов (PEP 8)
Стандартная конвенция разделяет импорты на три группы:
# 1. Standard library imports
import os
import sys
from pathlib import Path
# 2. Third-party library imports
import requests
import numpy as np
from pydantic import BaseModel
# 3. Local application imports
from myapp.models import User
from myapp.utils import validate
Инструменты вроде isort и ruff автоматизируют сортировку импортов.
Циклические импорты
Циклические импорты возникают, когда два модуля импортируют друг друга. Это частая ошибка новичков:
# a.py
from b import func_b # Tries to import b, which imports a...
def func_a():
return func_b()
# b.py
from a import func_a # Circular dependency!
def func_b():
return func_a()
Решения:
- Вынести общий код в третий модуль
- Использовать отложенный импорт (внутри функции)
- Пересмотреть архитектуру
# Lazy import — breaks the cycle
def func_a():
from b import func_b # Import at call time, not at module load
return func_b()