18

Относительные импорты в Python 3

14

Я хочу импортировать функцию из другого файла в одном каталоге.

Обычно получается использовать один из следующих вариантов:

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 ответ(ов)

3

Чтобы сделать относительные импорты рабочими в 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.

0

Я столкнулся с этой проблемой. Хаки-решение заключается в импорте через блок 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()

Такой подход позволяет избежать проблем с импортом в зависимости от контекста выполнения модуля. Если запускать модуль напрямую, будет использоваться обычный импорт. При импорте в другой модуль будет использоваться относительный импорт.

0

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.

Вот и все.

0

Для пользователей 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. Он неправ и код работает нормально, несмотря на то, что он сообщает.

0

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

Этот код добавит директорию с вашим пакетом в системный путь, чтобы вы могли импортировать нужные функции без проблем.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь