使用python3

时间:2015-12-27 07:14:20

标签: python jupyter-notebook relative-import

我的目录结构类似于以下

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文件(即使是空白文件),那么这样做是不错的,但这样做不足以解决问题。

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

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

9 个答案:

答案 0 :(得分:108)

我在this notebook中有几乎与你相同的例子,我想以干燥的方式说明相邻模块功能的用法。

我的解决方案是通过在笔记本中添加这样一个片段来告诉Python其他模块导入路径:

localStorage

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

setItem()

请注意,如果您还没有这些文件,则需要将空getItem()个文件添加到 project1 / lib / 文件夹中。

答案 1 :(得分:15)

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

项目层次结构如下:

├── 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 Notebook可以解析cd命令。请注意,这不会使用Python Notebook魔术。它只是在没有预先%bash的情况下工作。

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

    In [1]: cd /home/jovyan

    In [2]: from lib.postgres import database_connection

答案 2 :(得分:5)

到目前为止,已接受的答案对我来说效果最好。但是,我一直担心的是,在一种可能的情况下,我可能会将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.pynotebooks/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

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

答案 3 :(得分:4)

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

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

您只想要该文件的某些功能

from lib.store_load import your_function_name

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

答案 4 :(得分:1)

这是我的2美分:

导入系统

映射模块文件所在的路径。就我而言,它是台式机

sys.path.append('/ Users / John / Desktop')

要么导入整个映射模块,要么必须使用.notation来映射诸如mapping.Shipping()之类的类。

导入映射#mapping.py是我的模块文件的名称

shipit = mapping.Shipment()#Shipment是我需要在映射模块中使用的类的名称

或从映射模块导入特定的类

从映射导入映射

shipit = Shipment()#现在您不必使用.notation

答案 5 :(得分:0)

我自己研究此主题并阅读了答案,我建议使用path.py library,因为它提供了用于更改当前工作目录的上下文管理器。

然后您会得到类似的东西

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

由于该解决方案使用上下文管理器,因此无论内核在单元之前处于何种状态,以及导入库代码引发了什么异常,都可以保证返回到先前的工作目录。

答案 6 :(得分:0)

我发现python-dotenv可以非常有效地解决此问题。您的项目结构最终会稍有变化,但是笔记本中的代码在各个笔记本中都更加简单且一致。

为您的项目做一些安装。

pipenv install python-dotenv

然后,项目更改为:

├── .env (this can be empty)
├── ipynb
│   ├── 20170609-Examine_Database_Requirements.ipynb
│   └── 20170609-Initial_Database_Connection.ipynb
└── lib
    ├── __init__.py
    └── postgres.py

最后,您的导入更改为:

import os
import sys

from dotenv import find_dotenv


sys.path.append(os.path.dirname(find_dotenv()))

此软件包的+1是您的笔记本可以位于多个目录中。 python-dotenv将在父目录中找到最接近的一个并使用它。此方法的+2是jupyter将在启动时从.env文件加载环境变量。双重打击。

答案 7 :(得分:0)

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

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

env PYTHONPATH=`pwd` jupyter notebook

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

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

答案 8 :(得分:0)

对于那些像我一样不理解解决方案的人,您必须根据具体问题的需要深入到目录中。此错误的解决方案:

<块引用>

没有名为“your_folder”的模块

我使用的笔记本在:

C:\Users\vojte\projects\predicta\EDA\apartments\EDA.ipynb

我想导入这个笔记本:

C:\Users\vojte\projects\functions\functions.ipynb

我不得不将@metakermit 上面的解决方案修改为这个解决方案:

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 functions import functions as f

您可以检查 sys.path 以获取附加目录。在这个例子中,附加了这一行:

C:\\Users\\vojte\\projects