Подтвердить что ты не робот

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

У меня есть следующий каталог:

mydirectory
├── __init__.py
├── file1.py 
└── file2.py

У меня есть функция f, определенная в файле file1.py.

Если в файле2.py я делаю

from .file1 import f

Я получаю следующую ошибку:

SystemError: родительский модуль '' не загружен, не может выполнять относительные импорт

Почему? И как заставить его работать?

4b9b3361

Ответ 1

так как file1 и file2 находятся в одном каталоге, вам даже не нужно иметь файл __init__.py. Если вы собираетесь увеличивать масштаб, оставьте его там.

Чтобы импортировать что-то в файл в том же каталоге, просто сделайте это

from file1 import f

i.e, вам не нужно делать относительный путь .file1, потому что они находятся в одном каталоге.

Если ваша основная функция, script или что-то еще, которое будет запускать все приложение, находится в другом каталоге, тогда вам нужно будет сделать все относительно того, где это выполняется.

Ответ 2

Запуск модулей внутри пакета в качестве исполняемых файлов является плохой практикой.

Когда вы разрабатываете что-то, вы либо создаете библиотеку, которая предназначена для импорта другими программами, и поэтому не имеет смысла разрешать непосредственное выполнение своих подмодулей или вы создаете исполняемый файл, и в этом случае нет оснований для сделайте его частью пакета.

Вот почему в setup.py вы различаете пакеты и скрипты. Пакеты будут находиться под site-packages, в то время как скрипты будут установлены под /usr/bin (или аналогичным расположением в зависимости от ОС).

Моя рекомендация, таким образом, должна использовать следующий макет:

/
├── mydirectory
|    ├── __init__.py
|    ├── file1.py 
└── file2.py

Где file2.py импортирует file1.py как любой другой код, который хочет использовать библиотеку mydirectory, с абсолютным импортом:

from mydirectory.file1 import f

Когда вы пишете setup.py script для проекта, вы просто перечисляете mydirectory как пакет и file2.py как script, и все будет работать. Не нужно возиться с sys.path.

Если вы когда-либо, по какой-то причине, действительно хотите запустить подмодуль пакета, правильный способ сделать это - использовать переключатель -m:

python -m mydirectory.file1

Загружает весь пакет, а затем выполняет модуль как script, позволяя относительному импорту добиться успеха.

Я бы лично избегал этого. Также потому, что многие люди даже не знают, что вы можете это сделать, и в итоге получите ту же ошибку, что и вы, и подумайте, что пакет сломан.


Относительно принятого в настоящее время ответа, в котором говорится, что вы должны использовать неявный относительный импорт from file1 import f, потому что он будет работать, поскольку они находятся в одном каталоге:

Это неверно!

  • Он не будет работать в python3, где неявный относительный импорт будет запрещен и, безусловно, сломается, если вы установили модуль file1 (поскольку он будет импортирован вместо вашего модуля!).
  • Даже если он работает, file1 не будет рассматриваться как часть пакета mydirectory. Это может иметь значение.

    Например, если file1 использует pickle, имя пакета важно для правильной загрузки/выгрузки данных.

Ответ 3

При запуске исходного файла python запрещается импортировать другой файл, находящийся в текущем пакете, используя относительный импорт.

В документации говорится:

Обратите внимание, что относительный импорт основан на имени текущего модуля. Поскольку имя основного модуля всегда "__main__", модули, предназначенные для использования в качестве основного модуля приложения Python, должны всегда использовать абсолютный импорт.

Итак, как сказал @mrKelley, вам нужно использовать абсолютный импорт в такой ситуации.

Ответ 4

myproject/

mypackage
├── __init__.py
├── file1.py
├── file2.py 
└── file3.py

mymainscript.py

Пример импорта из одного файла в другой

#file1.py
from myproject import file2
from myproject.file3 import MyClass

Импортируйте пример пакета в mainccript

#mymainscript.py
import mypackage

https://docs.python.org/3/tutorial/modules.html#packages

https://docs.python.org/3/reference/import.html#regular-packages

https://docs.python.org/3/reference/simple_stmts.html#the-import-statement

https://docs.python.org/3/glossary.html#term-import-path

Переменная sys.path представляет собой список строк, определяющих путь поиска интерпретаторов для модулей. Он инициализируется путём по умолчанию, взятым из переменной окружения PYTHONPATH или из встроенного значения по умолчанию, если PYTHONPATH не установлен. Вы можете изменить его, используя стандартные операции с списком:

import sys
sys.path.append('/ufs/guido/lib/python')
sys.path.insert(0, '/ufs/guido/myhaxxlib/python')

Вставка его в начале имеет преимущество, гарантирующее, что поиск пути выполняется перед другими (даже встроенными) в случае конфликтов имен.