LoginSignup
21
16

More than 3 years have passed since last update.

Jupyter notebook で他の notebook の特定のセルをインポートする

Last updated at Posted at 2020-05-04

はじめに

Jupyter notebook でデータ分析なり実験なりしていると、分析用のクラス等を定義した他の notebook の特定のセルをインポートしたいなんてことがよくあります。うまくいきそうな方法を2つほど紹介します。

1. %%writefile マジックコマンド

一番手軽な方法はマジックコマンドの%%writefileを使うことです。セルの先頭に%%writefile filenameとすることでfilenameにセルの内容がコピーされます。あとは他の notebook からfilenameをインポートするだけです。

他のnotebookから読み込みたいセル
%%writefile hoge.py
class Hoge:
    def __init__(self):
        print('Hello from Hoge')
出力された他のnotebookのセルの内容を読み込んで利用する
import hoge

x = hoge.Hoge()

2. 頑張って自作する

公式が「それみんなやりたがるし、public な API だけでそんな苦労しなくてもできるよ!」って言ってるだったらコマンドひとつでできるようにしておいてほしいのでリンク先のコードを参考に自作します1

リンク先のコードはモジュールとして読み込んだ notebook のコードセルをすべて実行してしまうので、コードセルの先頭にあるコメントに# modcellと書いてあるセルを識別して読み込むように改造してみました。

modcellというライブラリとして公開したのでもしよければ使ってください。

(探せば私よりも clever なライブラリ作ってる人がいるはずなので、もしいいライブラリを他にご存知の方がいらっしゃいましたら教えていただけると幸いです)

Usage

modcellsys.pathに登録されているディレクトリに存在する拡張子.ipynbのファイルを探索の対象とし、コードセルの(マジックコマンドを除いた)先頭の行に書かれている# modcellというコメントを識別してモジュールとしてインポートします。

インストール

pip install modcell

modcell のインポート

modcellモジュールをインポートすると.ipynbを読み込むためのローダーがsys.meta_pathに追加されます。以降は拡張子が.ipynbのファイルをインポートすることが可能になります。

import modcell as mods

.ipynb ファイルをインポート

通常のモジュールと同じように拡張子を省いた名前でインポートすると、(IPythonのマジックコマンドを除いた)セルの先頭に# modcellと書かれたセルをすべてインポートします。

importされる側のnotebook(test_module.ipynb)のとあるセル
# modcell
class TestModule:
    def __init__(self):
        pass

    def hello(self):
        print('Hello from TestModule')
importする側のnotebookのとあるセル
import test_module as mod

x = mod.TestModule()
x.hello()

modcellは IPython のマジックコマンド(!または%で始まる行)を無視します。つまりmodcellによるインポートが行われたとき%autoreload%%timeは実行されません。

またmodcell自体が%autoreloadとかに対応してるかはテストしてないので知りません。Restart and Run allすれば他の notebook を再読み込みしてくれることは確認しております。

コンパイル

%%writefileの上位互換です。インポートしたすべてのセルをファイルに出力します。

import modcell as mods
import test_module

with open('module.py', mode='w') as f:
    mods.compile(out=f, source_info=True)

以下のようにフォーマットされます。

module.py
# test_module.ipynb ---------
# ---

class TestModule:
    def __init__(self):
        pass

    def hello(self):
        print('Hello from TestModule')

# ---
# --------- test_module.ipynb

デバッグなどの観点からセルに対応した区切りコメントが出力されますが、邪魔なときはsource_info=Falseを指定すれば出力されません。

.ipynb から特定のタグを持つセルだけをインポート

セルの先頭に書くコメントを

test_module.ipynbのとあるセル
# modcell: tagname
class TestModule:
    def __init__(self):
        pass

    def hello(self):
        print('Hello from TestModule')

の構文にすることで各セルにひとつだけタグをつけることができます。インポートする場合は

import modcell as mods

mod = mods._import('test_module', tag='tagname')

x = mod.TestModule().hello()

とすることでtagnameというタグがついたセルだけを読み込むことができます。

複数のモジュールファイルへのコンパイル

modcellはデフォルトでmodcell.ModCellクラスのインスタンスをひとつ生成しており、すべての関数はそのデフォルトインスタンスのメソッドとして実行されています。このデフォルトインスタンスはmodcell.default()関数で取得することができますが、ユーザが操作することは非推奨です。

ユーザは必要に応じて複数のModCellインスタンスを作成することが可能です。

import modcell as mods

mod_1 = mods.ModCell()
mod_2 = mods.ModCell()
...

つまり以下のようなコードを実行することで、複数の.ipynbから複数の.pyファイルを生成できます。

import modcell as mods

mod_debug = mods.ModCell()
nb1_debug = mod_debug._import('notebook_1', tag='debug')
nb2 = mod_debug._import('notebook_2')
nb3 = mod_debug._import('notebook_3')
with open('../module/mod_debug.py', mode='w') as f:
    mod_debug.compile(out=f)

mod_test = mods.ModCell()
nb1_test = mod_test._import('notebook_1', tag='test')
nb4 = mod_test._import('notebook_4')
with open('../module/mod_test.py', mode='w') as f:
    mod_test.compile(out=f)

おまけ

もうひとつ思いついた方法として「IPython のカーネルにアタッチして他の notebook のコードセルの中身を引っ張ってくる」というものがあったが、どうもコードセルの中身を保持しているのはフロントエンドの JavaScript であって IPython のカーネルはパーサーにかけた後のコードしか保持していないらしいのでボツ案となった。


  1. リンク先の内容については @tdual さんが記事を書いてくださっています。調べたところ @tdual さんの記事で公開されているコード(つまり公式で紹介されているコード)は Python 3.4 あたりから非推奨になったレガシーなコードが混じっているようなので、modcell では若干の手直しが入っています。もしバージョンの関係などで modcell がうまく動かないときは @tdual さんのコードを試すとよいと思います。 

21
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
16