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

Импортировать локальную функцию из модуля, размещенного в другом каталоге с относительным импортом в ноутбуке jupyter, используя python3

У меня есть структура каталогов, аналогичная следующей

meta_project
    project1
        __init__.py
        lib
            module.py
            __init__.py
    notebook_folder
        notebook.jpynb

При работе в notebook.jpynb, если я попытаюсь использовать относительный импорт для доступа к функции function() в module.py с помощью:

from ..project1.lib.module import function

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

SystemError                               Traceback (most recent call last)
<ipython-input-7-6393744d93ab> in <module>()
----> 1 from ..project1.lib.module import function

SystemError: Parent module '' not loaded, cannot perform relative import

Есть ли способ заставить это работать, используя относительный импорт?

Примечание. Сервер ноутбука создается на уровне каталога meta_project, поэтому он должен иметь доступ к информации в этих файлах.

Обратите также внимание, что, по крайней мере, как первоначально предполагалось, project1 не рассматривался как модуль и, следовательно, не имеет файла __init__.py, это просто предназначалось как каталог файловой системы. Если решение проблемы требует рассмотрения его как модуля и включает в себя файл __init__.py (даже пустой), это нормально, но этого недостаточно для решения проблемы.

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


Изменить: это в отличие от Относительный импорт в Python 3, в котором говорится об относительном импорте в Python 3 в целом и, в частности, о запуске script изнутри каталог пакетов. Это связано с работой в ноутбуке jupyter, который пытается вызвать функцию в локальном модуле в другом каталоге, который имеет как разные общие, так и конкретные аспекты.

4b9b3361

Ответ 1

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

Мое решение состояло в том, чтобы сообщить Python об этом пути импорта дополнительного модуля, добавив в блокнот такой сниппет:

import os
import sys
module_path = os.path.abspath(os.path.join('..'))
if module_path not in sys.path:
    sys.path.append(module_path)

Это позволяет вам импортировать нужную функцию из иерархии модуля:

from project1.lib.module import function
# use the function normally
function(...)

Обратите внимание, что необходимо добавить пустые __init__.py файлы в project1 и lib/folders, если у вас их уже нет.

Ответ 2

Пришел сюда в поисках лучших практик абстрагирования кода для субмодулей при работе в Блокнотах. Я не уверен, что есть лучшая практика. Я предлагал это.

Иерархия проекта как таковая:

├── ipynb
│   ├── 20170609-Examine_Database_Requirements.ipynb
│   └── 20170609-Initial_Database_Connection.ipynb
└── lib
    ├── __init__.py
    └── postgres.py

И из 20170609-Initial_Database_Connection.ipynb:

    In [1]: cd ..

    In [2]: from lib.postgres import database_connection

Это работает, потому что по умолчанию блокнот Jupyter может анализировать команду cd. Обратите внимание, что это не использует магию Python Notebook. Это просто работает без предоплаты %bash.

Учитывая, что 99 раз из 100 я работаю в Docker, используя один из образов Project Jupyter Docker, следующая модификация идемпотентна

    In [1]: cd /home/jovyan

    In [2]: from lib.postgres import database_connection

Ответ 3

До сих пор принятый ответ работал лучше всего для меня. Однако меня всегда беспокоило то, что существует вероятный сценарий, когда я мог бы преобразовать каталог notebooks в подкаталоги, требуя замены module_path в каждой записной книжке. Я решил добавить файл python в каждый каталог ноутбука, чтобы импортировать необходимые модули.

Таким образом, имея следующую структуру проекта:

project
|__notebooks
   |__explore
      |__ notebook1.ipynb
      |__ notebook2.ipynb
      |__ project_path.py
   |__ explain
       |__notebook1.ipynb
       |__project_path.py
|__lib
   |__ __init__.py
   |__ module.py

Я добавил файл project_path.py в каждый подкаталог ноутбука (notebooks/explore и notebooks/explain). Этот файл содержит код для относительного импорта (из @metakermit):

import sys
import os

module_path = os.path.abspath(os.path.join(os.pardir, os.pardir))
if module_path not in sys.path:
    sys.path.append(module_path)

Таким образом, мне просто нужно сделать относительный импорт в файле project_path.py, а не в записных книжках. Файлы с записных книжек затем нужно будет импортировать project_path перед импортом lib. Например, в 0.0-notebook.ipynb:

import project_path
import lib

Предостережение заключается в том, что изменение импорта не сработает. ЭТО НЕ РАБОТАЕТ:

import lib
import project_path

Таким образом, необходимо соблюдать осторожность при импорте.

Ответ 4

Изучая эту тему самостоятельно и прочитав ответы, я рекомендую использовать библиотеку path.py, поскольку она предоставляет контекстный менеджер для изменения текущего рабочего каталога.

Затем у вас есть что-то вроде

import path
if path.Path('../lib').isdir():
    with path.Path('..'):
        import lib

Хотя вы можете просто опустить оператор isdir.

Здесь я добавлю операторы печати, чтобы было легче следить за тем, что происходит

import path
import pandas

print(path.Path.getcwd())
print(path.Path('../lib').isdir())
if path.Path('../lib').isdir():
    with path.Path('..'):
        print(path.Path.getcwd())
        import lib
        print('Success!')
print(path.Path.getcwd())

который выводит в этом примере (где lib находится в /home/jovyan/shared/notebooks/by-team/data-vis/demos/lib):

/home/jovyan/shared/notebooks/by-team/data-vis/demos/custom-chart
/home/jovyan/shared/notebooks/by-team/data-vis/demos
/home/jovyan/shared/notebooks/by-team/data-vis/demos/custom-chart

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