ImportError: cannot import name 'User' from 'app.models' означает, что Python нашёл и начал загружать app.models, но не смог получить из него имя User. Обычно имя удалили, переименовали, не экспортировали из __init__.py или ещё не успели создать из-за циклического импорта.
Если в последней строке написано ModuleNotFoundError: No module named 'app', это другая ветка диагностики: сам модуль не найден. Проверка venv, sys.path и установки пакета разобрана в инструкции по ModuleNotFoundError. Не запускайте pip install только потому, что видите cannot import name.
Цикл импорта застал модуль недописанным
a уже есть в sys.modules, но его код ещё не дошёл до class User. Поэтому обратный импорт видит модуль, но не имя.Чем ImportError отличается от ModuleNotFoundError
ModuleNotFoundError — потомок ImportError, но сообщения описывают разные этапы импорта:
ModuleNotFoundError: No module named 'reports'
Python не смог найти reports по путям импорта. Сначала проверяют окружение, имя модуля и sys.path.
ImportError: cannot import name 'render_pdf' from 'reports' (/project/reports.py)
reports найден, и Python перешёл к поиску render_pdf в нём, но не нашёл это имя. Здесь проверяют имя, версию API, файл загруженного модуля и возможный цикл.
Точный путь в скобках особенно полезен: он показывает, какой файл Python принял за reports.
Проверьте, есть ли имя в модуле
Допустим, раньше библиотека экспортировала Client, а после обновления класс переименовали в ApiClient. Тогда старый импорт падает, хотя сам пакет установлен:
from service import Client
Проверьте модуль в том же интерпретаторе, которым запускаете проект:
python -c "import service; print(service.__file__); print(hasattr(service, 'Client')); print([name for name in dir(service) if 'Client' in name])"
Команда ответит на три вопроса: откуда загружен модуль, есть ли в нём точное имя и есть ли похожее. Затем сверьте текущую версию библиотеки с её документацией и историей изменений. Не подменяйте удалённый класс случайным одноимённым пакетом.
Если имя живёт в подмодуле, импорт должен вести туда:
service/
├── __init__.py
└── client.py # здесь объявлен ApiClient
from service.client import ApiClient
Вариант from service import ApiClient сработает, только если service/__init__.py сам свяжет это имя, например from .client import ApiClient.
Что на самом деле делает all
__all__ управляет набором публичных имён для импорта со звёздочкой:
# api.py
__all__ = ["public_name"]
public_name = 1
internal_name = 2
from api import *
print(public_name) # 1
# internal_name здесь не связано
Но __all__ не запрещает явный импорт:
from api import internal_name
print(internal_name) # 2
Поэтому добавлять существующее имя в __all__ ради исправления from api import internal_name не нужно. Если явный импорт падает, атрибут либо не связан в модуле, либо загрузка не дошла до его объявления.
Как возникает partially initialized module
Минимальный цикл состоит из трёх файлов:
cycle_demo/
├── main.py
├── a.py
└── b.py
# main.py
from a import User
print(User("Ada"))
# a.py
from b import format_name
class User:
def __init__(self, name):
self.name = format_name(name)
# b.py
from a import User
def format_name(name):
return name.upper()
Запуск из cycle_demo:
python main.py
заканчивается строкой:
ImportError: cannot import name 'User' from 'a' (.../a.py)
Порядок выполнения важен:
main.pyзапрашиваетUserизa.- Python создаёт объект модуля
a, записывает его вsys.modulesи только потом начинает выполнятьa.pyсверху вниз. - Первая строка
a.pyначинает загрузкуb.py. - Первая строка
b.pyснова импортируетa. Python находит его вsys.modulesи не запускаетa.pyвторой раз. - Имя
Userещё не создано: выполнениеa.pyостановилось на первой строке, аclass Userстоит ниже. Поэтомуfrom a import UserподнимаетImportError.
CPython может добавить фразу partially initialized module, особенно для цикла внутри пакета, но этот диагностический суффикс не гарантирован. Проверяйте весь traceback: цепочка может проходить не напрямую A↔B, а через A→B→C→A.
Три способа убрать циклический импорт
1. Вынести общую зависимость в третий модуль
Это основное исправление, если A и B нужен один и тот же тип, константа или функция. Оба модуля зависят от общего, а не друг от друга:
app/
├── models.py
├── formatter.py
└── main.py
# models.py
class User:
def __init__(self, name):
self.name = name
# formatter.py
from models import User
def format_user(user: User):
return user.name.upper()
# main.py
from formatter import format_user
from models import User
print(format_user(User("Ada")))
Теперь граф зависимостей однонаправлен: main → formatter → models и main → models.
2. Импортировать модуль, а не имя
from a import User требует User прямо во время импорта. import a может разорвать эту конкретную гонку, если код обратится к a.User только после завершения обоих модулей:
# a.py
import b
class User:
def __init__(self, name):
self.name = b.format_name(name)
# b.py
import a
def format_name(name):
return name.upper()
def is_user(value):
return isinstance(value, a.User)
Тела функций выполнятся позже, когда a.py уже создал User. Если b.py попытается вычислить a.User на верхнем уровне, замена формы импорта не поможет: вместо ImportError может появиться AttributeError.
3. Перенести импорт внутрь функции
Локальный импорт откладывает загрузку до вызова функции:
def build_report(data):
from .formatter import format_report
return format_report(data)
К этому моменту другие модули могут уже завершить инициализацию. Но зависимость становится менее заметной, а ошибка импорта переносится из старта программы в конкретный вызов. Используйте этот вариант как осознанный компромисс для редкого пути или старого кода. Если два модуля по-прежнему знают друг о друге, лучше вынести общую часть.
Относительный импорт без контекста пакета
Другое типичное сообщение выглядит так:
ImportError: attempted relative import with no known parent package
Оно воспроизводится, если запустить файл пакета напряму:
shop-project/
└── shop/
├── __init__.py
├── cli.py
└── prices.py
# shop/prices.py
def total():
return 120
# shop/cli.py
from .prices import total
print("package:", __package__)
print("spec:", __spec__.name)
print("total:", total())
Неверная команда:
python shop/cli.py
При прямом запуске файл становится модулем __main__, но интерпретатор не знает его полного пакетного имени. Для скрипта, заданного путём к файлу, __spec__ и __package__ на CPython 3.14.5 равны None. Ведущая точка в .prices не имеет точки отсчёта.
Запустите модуль из shop-project — корня проекта, внутри которого лежит пакет shop:
cd shop-project
python -m shop.cli
Результат:
package: shop
spec: shop.cli
total: 120
Ключ -m ищет shop.cli как модуль и сохраняет его пакетный контекст. В CPython 3.14 относительный импорт опирается прежде всего на __spec__.parent; __package__ ещё служит запасным механизмом, но этот путь устарел. Не присваивайте эти атрибуты вручную ради обхода ошибки: команда python -m package.module описывает намерение явно и не зависит от внутреннего запасного пути.
Проверьте, не затенил ли локальный файл библиотеку
При обычном запуске Python добавляет в начало пути поиска каталог скрипта, а для python -m — текущий каталог. Поэтому файл проекта может перехватить имя стандартной или сторонней библиотеки. Ключи -P и -I отключают это поведение:
project/
├── main.py
└── requests.py # локальный файл
# main.py
from requests import Session
Если в локальном requests.py нет Session, импорт покажет cannot import name, хотя нужный дистрибутив может быть установлен. Найдите первый подходящий файл без выполнения его кода:
python -c "import importlib.util; print(importlib.util.find_spec('requests').origin)"
Или посмотрите файл уже импортированного модуля:
python -c "import requests; print(requests.__file__)"
Путь вида /project/requests.py подтверждает затенение. Переименуйте локальный файл вроде http_client.py и повторите команду. Так же проверяют json.py, typing.py, email.py, logging.py, random.py и каталоги с такими именами.
Короткий алгоритм диагностики
- Прочитайте последнюю строку и весь traceback.
No module namedиcannot import nameтребуют разных проверок. - Посмотрите путь в скобках или напечатайте
module.__file__. Неожиданный путь выдаёт затенение или другое окружение. - Проверьте точное написание имени, модуль, где оно объявлено, и API установленной версии. Помните, что
__all__ограничиваетimport *, а не явныйfrom module import name. - Если есть
partially initialized module, пройдите цепочку файлов в traceback и найдите обратное ребро. Лучший фикс — общий модуль без зависимости от A и B. - Если видите
attempted relative import with no known parent package, запуститеpython -m package.moduleиз каталога, который содержит пакет.
Перехватывать ImportError ради продолжения запуска обычно не нужно: без обязательного модуля программа остаётся в частично известном состоянии. Исключение — явно необязательная зависимость с полноценной запасной веткой. Если нужно обрабатывать такую ситуацию, перехватывайте узко и учитывайте, что ModuleNotFoundError наследуется от ImportError. Базовые правила перехвата разобраны в статье про try/except.