web-dev-qa-db-ja.com

python3を使用してjupyterノートブックで相対的なインポートを行う別のディレクトリにあるモジュールからローカル関数をインポートします

私は次のようなディレクトリ構造を持っています

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

notebook.jpynbで作業しているときに、相対インポートを使用してmodule.pyの関数function()にアクセスしようとすると:

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の相対インポートについて、特に内部からスクリプトを実行するパッケージディレクトリ。これは、一般的な側面と特定の側面が異なる別のディレクトリにあるローカルモジュールの関数を呼び出そうとするjupyterノートブック内での作業に関係しています。

77
mpacer

このノートブック であなたとほとんど同じ例がありましたが、ここではDRYの方法で隣接モジュールの関数の使用法を説明したかったのです。

私の解決策は、このようなスニペットをノートブックに追加することにより、その追加モジュールインポートパスを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 /フォルダーに追加する必要があることに注意してください。

110
metakermit

ノートブックで作業するときにサブモジュールにコードを抽象化するベストプラクティスを探してここに来ました。ベストプラクティスがあるかどうかはわかりません。私はこれを提案しています。

そのようなプロジェクト階層:

├── 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回を考慮すると、次の変更isべき等

    In [1]: cd /home/jovyan

    In [2]: from lib.postgres import database_connection
16
Joshua Cook

これまでのところ、受け入れられた答えは私にとって最もうまく機能しました。ただし、私の懸念は常に、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ファイル内で相対的なインポートを行うだけです。ノートブックファイルは、libをインポートする前に、project_pathをインポートするだけで済みます。たとえば、0.0-notebook.ipynbの場合:

import project_path
import lib

ここでの注意点は、インポートの取り消しが機能しないことです。これは動作しません:

import lib
import project_path

したがって、インポート時には注意が必要です。

6
Gerges