Относительные импорты в Python 3
Я хочу импортировать функцию из другого файла в одном каталоге.
Обычно получается использовать один из следующих вариантов:
from .mymodule import myfunction
from mymodule import myfunction
Но один из них вызывает одну из следующих ошибок:
ImportError: attempted relative import with no known parent package
ModuleNotFoundError: No module named 'mymodule'
SystemError: Parent module '' not loaded, cannot perform relative import
Почему это происходит?
5 ответ(ов)
Чтобы сделать относительные импорты рабочими в Python 3.6, добавьте следующий код в ваш файл __init__.py
вашего пакета:
# Для относительных импортов в Python 3.6
import os, sys; sys.path.append(os.path.dirname(os.path.realpath(__file__)))
Предполагая, что ваша структура пакета выглядит следующим образом:
├── project
│ ├── package
│ │ ├── __init__.py
│ │ ├── module1.py
│ │ └── module2.py
│ └── setup.py
Теперь вы можете использовать обычные импорты в вашем пакете, например:
# в module2.py
from module1 import class1
Этот подход будет работать как в Python 2, так и в Python 3.
Я столкнулся с этой проблемой. Хаки-решение заключается в импорте через блок if/else следующим образом:
#!/usr/bin/env python3
#myothermodule
if __name__ == '__main__':
from mymodule import as_int
else:
from .mymodule import as_int
# Экспортируемая функция
def add(a, b):
return as_int(a) + as_int(b)
# Тестовая функция для модуля
def _test():
assert add('1', '1') == 2
if __name__ == '__main__':
_test()
Такой подход позволяет избежать проблем с импортом в зависимости от контекста выполнения модуля. Если запускать модуль напрямую, будет использоваться обычный импорт. При импорте в другой модуль будет использоваться относительный импорт.
TL;DR
Можно использовать относительный импорт модулей только внутри другого модуля в том же пакете.
Уточнение концепции
Мы часто видим примеры кода в книгах, документации и статьях, которые показывают, как использовать относительный импорт модуля, но когда мы пытаемся это сделать, возникает ошибка.
Причина в том, что, проще говоря, мы не запустили код так, как это ожидает механизм модулей Python, даже если код написан совершенно правильно. Это похоже на некую проблему с выполнением.
Загрузка модуля зависит от того, как вы запускаете код. Это и является источником путаницы.
Что такое модуль?
Модуль — это Python-файл, но только тогда, когда он импортируется из другого файла. Если у вас есть файл mod.py
, является ли он модулем? Да и нет: если вы запускаете python mod.py
, он не является модулем, так как не импортируется.
Что такое пакет?
Пакет — это папка, содержащая Python-модули.
Кстати, __init__.py
не требуется с Python 3.3, если вам не нужна инициализация пакета или авто-загрузка подмодулей. Не обязательно помещать пустой __init__.py
в каталог.
Это подтверждает, что пакет — это просто папка, пока в ней есть файлы, которые имплементируются.
Реальный ответ
Теперь это описание становится яснее.
Вы можете использовать относительный импорт модулей только внутри другого модуля в том же пакете.
Рассмотрим директорию:
. CWD
|-- happy_maker.py # содержимое: print('Sends Happy')
`-- me.py # содержимое: from . import happy_maker
Запуская python me.py
, мы получаем ошибку attempted relative import with no known parent package
.
me.py
запускается напрямую, это не модуль, и мы не можем использовать относительный импорт в нем.
Решение 1
Используйте import happy_maker
вместо from . import happy_maker
.
Решение 2
Переключите рабочую директорию на родительскую папку.
. CWD
|-- happy
| |-- happy_maker.py
`-- me.py
Запустив python -m happy.me
, мы можем использовать относительный импорт, так как находимся в каталоге, содержащем happy
, который теперь является пакетом, а me.py
и happy_maker.py
- модулями. Используем -m
, чтобы запустить модуль как скрипт.
Питоновская идиома
. CWD
|-- happy
| |-- happy_maker.py # содержимое: print('Sends Happy')
| `-- me.py # содержимое: from . import happy_maker
`-- main.py # содержимое: import happy.me
Эта структура представляет собой питоновскую идиому. main
— это наш скрипт, это лучшее практическое решение в Python. Наконец, мы пришли к этому.
Соседи или Дедушки/Бабушки
Еще одна распространенная задача:
.
|-- happy
| |-- happy_maker.py
| `-- me.py
`-- sad
`-- sad_maker.py
Если мы хотим импортировать sad_maker
в me.py
, как это сделать?
Сначала нам нужно сделать так, чтобы happy
и sad
находились в одном и том же пакете, поэтому нужно подняться на уровень вверх. Затем в me.py
пишем from ..sad import sad_maker
.
Вот и все.
Для пользователей PyCharm:
У меня также возникала ошибка ImportError: attempted relative import with no known parent package
, потому что я пытался использовать нотацию .
для подавления ошибки парсинга в PyCharm. PyCharm неправильно сообщает, что не может найти:
lib.thing import function
Если изменить это на:
.lib.thing import function
ошибка действительно исчезает, но при этом вы получаете вышеуказанную ImportError: attempted relative import with no known parent package
. Просто игнорируйте парсер PyCharm. Он неправ и код работает нормально, несмотря на то, что он сообщает.
TL;DR: к ответу @Aya, обновлено с использованием библиотеки pathlib
, и работает для Jupyter ноутбуков, где __file__
не определен:
Если вы хотите импортировать функцию my_function
, определённую в файле ../my_Folder_where_the_package_lives/my_package.py
относительно текущего расположения вашего кода, выполните следующее:
import os
import sys
import pathlib
PACKAGE_PARENT = pathlib.Path(__file__).parent
#PACKAGE_PARENT = pathlib.Path.cwd().parent # если вы находитесь в Jupyter ноутбуке
SCRIPT_DIR = PACKAGE_PARENT / "my_Folder_where_the_package_lives"
sys.path.append(str(SCRIPT_DIR))
from my_package import my_function
Этот код добавит директорию с вашим пакетом в системный путь, чтобы вы могли импортировать нужные функции без проблем.
Преобразование байтов в строку в Python 3
Импорт файлов из другой папки
Как вернуть ключи словаря в виде списка в Python?
UnicodeDecodeError: Кодек 'charmap' не может декодировать байт X в позиции Y: символ отображается как <неопределённый>
"TypeError: требуется объект, похожий на bytes, а не 'str' при обработке содержимого файла в Python 3"