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

mpa*_*cer 86 python relative-import jupyter-notebook

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

meta_project
    project1
        __init__.py
        lib
            module.py
            __init__.py
    notebook_folder
        notebook.jpynb
Run Code Online (Sandbox Code Playgroud)

notebook.jpynb我尝试使用相对导入来访问函数function()module.py使用:

from ..project1.lib.module import function
Run Code Online (Sandbox Code Playgroud)

我收到以下错误

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
Run Code Online (Sandbox Code Playgroud)

有没有办法让这个使用相对导入工作?

请注意,笔记本服务器在meta_project目录级别实例化,因此它应该可以访问这些文件中的信息.

另请注意,至少与最初的预期project1并未被视为模块,因此没有__init__.py文件,它只是作为文件系统目录.如果问题的解决方案需要将其视为一个模块并包含一个__init__.py文件(即使是空白文件),那么这样做是不错的,但这样做不足以解决问题.

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


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

met*_*mit 126

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

我的解决方案是通过向笔记本添加像这样的代码片段来告诉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)
Run Code Online (Sandbox Code Playgroud)

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

from project1.lib.module import function
# use the function normally
function(...)
Run Code Online (Sandbox Code Playgroud)

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

  • 这解决了能够使用或多或少的相对位置导入包的问题,​​但只是间接地.我碰巧认识Matthias Bussonier(@matt on SE)和Yuvi Panda(@yuvi on SE)正在开发https://github.com/ipython/ipynb,它将更直接地解决这个问题(例如,允许使用标准进行相对导入)导入包后的语法).我现在接受你的答案,当他们的解决方案完全准备好供其他人使用时,我可能会写一个关于如何使用它的答案,或者让其中一个人这样做. (6认同)
  • Python 3中不再需要空的__init__.py文件。 (5认同)

Jos*_*ook 20

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

项目层次结构如下:

??? ipynb
?   ??? 20170609-Examine_Database_Requirements.ipynb
?   ??? 20170609-Initial_Database_Connection.ipynb
??? lib
    ??? __init__.py
    ??? postgres.py
Run Code Online (Sandbox Code Playgroud)

来自20170609-Initial_Database_Connection.ipynb:

    In [1]: cd ..

    In [2]: from lib.postgres import database_connection
Run Code Online (Sandbox Code Playgroud)

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

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

    In [1]: cd /home/jovyan

    In [2]: from lib.postgres import database_connection
Run Code Online (Sandbox Code Playgroud)

  • 我喜欢这个解决方案,直到我不小心执行了两次 `cd ..`。 (2认同)

Ger*_*ges 10

到目前为止,已接受的答案对我来说效果最好。但是,我一直担心的是,在某些情况下,我可能会将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
Run Code Online (Sandbox Code Playgroud)

project_path.py在每个笔记本子目录(notebooks/explorenotebooks/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)
Run Code Online (Sandbox Code Playgroud)

这样,我只需要在project_path.py文件中而不是在笔记本中进行相对导入。然后,笔记本文件仅需要在导入project_path之前导入lib。例如在0.0-notebook.ipynb

import project_path
import lib
Run Code Online (Sandbox Code Playgroud)

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

import lib
import project_path
Run Code Online (Sandbox Code Playgroud)

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


Ale*_*g_O 9

如果您在 VS Code 中通过 Jupyter 扩展进行工作,我发现您现在可以在settings.json文件中设置此路径。

{
    "jupyter.notebookFileRoot": "/path/to/your/module/root"
}
Run Code Online (Sandbox Code Playgroud)

编辑:或者,更一般地将其设置在工作区根目录中:

{
    "jupyter.notebookFileRoot": "${workspaceFolder}"
}
Run Code Online (Sandbox Code Playgroud)


小智 8

我发现python-dotenv可以非常有效地解决这个问题。您的项目结构最终会略有变化,但笔记本中的代码更加简单并且在笔记本之间保持一致。

\n\n

对于您的项目,进行一些安装。

\n\n
pipenv install python-dotenv\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后,项目更改为:

\n\n
\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 .env (this can be empty)\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ipynb\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 20170609-Examine_Database_Requirements.ipynb\n\xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 20170609-Initial_Database_Connection.ipynb\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 lib\n    \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 __init__.py\n    \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 postgres.py\n
Run Code Online (Sandbox Code Playgroud)\n\n

最后,您的导入更改为:

\n\n
import os\nimport sys\n\nfrom dotenv import find_dotenv\n\n\nsys.path.append(os.path.dirname(find_dotenv()))\n
Run Code Online (Sandbox Code Playgroud)\n\n

这个包的优点是你的笔记本可以有几个目录深。python-dotenv 将在父目录中找到最近的一个并使用它。这种方法的优点是 jupyter 将在启动时从 .env 文件加载环境变量。双重打击。

\n


Vic*_*jas 5

我刚刚找到了这个漂亮的解决方案:

import sys; sys.path.insert(0, '..') # add parent folder path where lib folder is
import lib.store_load # store_load is a file on my library folder
Run Code Online (Sandbox Code Playgroud)

你只想要那个文件的一些功能

from lib.store_load import your_function_name
Run Code Online (Sandbox Code Playgroud)

如果 python 版本 >= 3.3 你不需要文件夹中的 init.py 文件

  • 我发现这非常有帮助。我要补充的是,应该添加以下修改 --&gt; `if ".." not in sys.path: ... sys.path.insert(0,"..")` (3认同)

elg*_*lge 5

这里的所有其他答案都取决于在笔记本中添加代码(!)

在我看来,将特定路径硬编码到笔记本代码中或以其他方式取决于位置是不好的做法,因为这使得以后重构代码变得非常困难。相反,我建议您在启动 Jupyter 笔记本服务器时将根项目文件夹添加到 PYTHONPATH,或者直接从项目文件夹中添加

env PYTHONPATH=`pwd` jupyter notebook

或者如果您是从其他地方启动它,请使用绝对路径

env PYTHONPATH=/Users/foo/bar/project/ jupyter notebook

  • 这实际上是这个问题的最佳答案。 (3认同)