关于python:使用python3从jupyter笔记本中相对导入的另一个目录中的模块导入本地函数

import local function from a module housed in another directory with relative imports in jupyter notebook using python3

我有一个类似于以下的目录结构

1
2
3
4
5
6
7
8
meta_project
    project1
        __init__.py
        lib
            module.py
            __init__.py
    notebook_folder
        notebook.jpynb

notebook.jpynb中工作时,如果我尝试使用相对导入来访问module.py中的函数function()

1
from ..project1.lib.module import function

我收到以下错误

1
2
3
4
5
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文件(甚至是空白文件),那么这样做是不错的,但这样做不足以解决问题。

我在机器和相对导入之间共享这个目录允许我在任何地方使用相同的代码,&amp;我经常使用笔记本进行快速原型设计,所以涉及黑客攻击绝对路径的建议不太可能有用。

编辑:这与Python 3中的相对导入不同,Python 3通常讨论Python 3中的相对导入,特别是从包目录中运行脚本。这与在jupyter笔记本中工作有关,试图在另一个目录中的本地模块中调用一个函数,该目录具有不同的一般和特定方面。


我在这个笔记本中有几乎和你一样的例子,我想以干燥的方式说明相邻模块功能的用法。

我的解决方案是通过向笔记本添加像这样的代码片段来告诉Python其他模块导入路径:

1
2
3
4
5
import os
import sys
module_path = os.path.abspath(os.path.join('..'))
if module_path not in sys.path:
    sys.path.append(module_path)

这允许您从模块层次结构导入所需的功能:

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

请注意,如果您还没有将空__init__.py文件添加到project1 /和lib /文件夹中,则需要添加它们。


来到这里寻找在笔记本中工作时将代码抽象到子模块的最佳实践。我不确定是否有最好的做法。我一直在提议这个。

项目层次结构如下:

1
2
3
4
5
6
├── ipynb
│&nbsp;&nbsp; ├── 20170609-Examine_Database_Requirements.ipynb
│&nbsp;&nbsp; └── 20170609-Initial_Database_Connection.ipynb
└── lib
    ├── __init__.py
    └── postgres.py

20170609-Initial_Database_Connection.ipynb

1
2
3
    In [1]: cd ..

    In [2]: from lib.postgres import database_connection

这是有效的,因为默认情况下,Jupyter Notebook可以解析cd命令。请注意,这不会使用Python Notebook魔术。它只是在没有预先%bash的情况下工作。

考虑到我使用Project Jupyter Docker图像之一在Docker中工作了100次中的99次,以下修改是幂等的

1
2
3
    In [1]: cd /home/jovyan

    In [2]: from lib.postgres import database_connection


到目前为止,接受的答案对我来说效果最好。但是,我一直担心的是,我可能会将notebooks目录重构为子目录,需要更改每个笔记本中的module_path。我决定在每个笔记本目录中添加一个python文件来导入所需的模块。

因此,具有以下项目结构:

1
2
3
4
5
6
7
8
9
10
11
12
project
|__notebooks
   |__explore
      |__ notebook1.ipynb
      |__ notebook2.ipynb
      |__ project_path.py
   |__ explain
       |__notebook1.ipynb
       |__project_path.py
|__lib
   |__ __init__.py
   |__ module.py

我在每个笔记本子目录(notebooks/explorenotebooks/explain)中添加了文件project_path.py。此文件包含相对导入的代码(来自@metakermit):

1
2
3
4
5
6
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文件中进行相对导入,而不是在笔记本中。然后,在导入lib之前,笔记本文件只需要导入project_path。例如在0.0-notebook.ipynb中:

1
2
import project_path
import lib

这里需要注意的是,逆转进口是行不通的。这不起作用:

1
2
import lib
import project_path

因此在进口期间必须小心。