モチベーション
- 開発段階ではJupyter Notebookがとても便利なので使って開発したい!
- コマンドラインから実行できる
.py
も作りたい! - でもそのためにはJupyterで定義した関数やクラスはコピペしなきゃいけないからめんどくさい!
そんなときに、Jupyter Notebookをmoduleとしてインポートできたら便利だと思いませんか?
この記事でもその方法が書かれていますが、これだとnotebook自体を一度実行しているので、重い処理を書いていたりするとインポートに時間がかかってしまったり、print
などの出力も出てしまいます。
そこで今回は、Jupyter notebookを実行せずに、そこで定義された関数やクラスのみをインポートする方法を紹介します!
Jupytextのインストール
まずJupytext
という、Jupyter notebookと同期した.pyファイルを生成してバージョン管理をしやすくするツールをインストールします。これ自体すごく便利なツールなので、インストールしておいて損はないと思います。
↓この記事がとてもわかりやすいので参考にしてください。
https://qiita.com/cfiken/items/8455383f32ee19dfbba3
記事の通りに進めて、Jupyter notebookと同期した.py
ファイルを作成します。
ちなみにこの.py
ファイルは、notebookの方を変更して保存すると勝手に同期してくれるので毎回生成し直す必要はありません。
今回は、sample.ipynbというnotebookを作成し、そこからsample.pyを生成しました。
sample.ipynbには関数&クラスの定義の他に、print
文を含んだ実行される処理も含まれています。これをそのままimportした場合は、このprint文も実行されることになります。
notebookのインポート
さてここから、notebookの関数とクラスだけをインポートしていきます。
コマンドラインから実行するためのrun.py
を以下のように書きました。
やってることとしては、.py
ファイルを読み込んで、必要な部分だけを残してコンパイルし直し、それをimportしています。
# notebookの関数とクラスのimport
import ast
import types
import sys
# ここには先ほど生成した.pyファイルを入れる
with open("sample.py") as f:
p = ast.parse(f.read())
for node in p.body[:]:
# 関数定義、クラス定義、import文以外のものは呼び出されないようにする
if not isinstance(node, (ast.FunctionDef, ast.ClassDef, ast.Import, ast.ImportFrom)):
p.body.remove(node)
module = types.ModuleType("mod")
code = compile(p, "mod.py", 'exec')
sys.modules["mod"] = module
exec(code, module.__dict__)
from mod import *
if __name__ == '__main__':
# sample.ipynbで定義した関数とクラスの実行
sample_class = SampleClass(10)
print("run.py: SampleClass", sample_class())
print("run.py: make_randint", make_randint(100))
これの実行結果は以下のようになります。sample.ipynb
やsample.py
の中のprint文は実行されずに関数とクラスだけインポートされていることがわかります!
$ python run.py
run.py: SampleClass [0 1 2 3 4 5 6 7 8 9]
run.py: make_randint 28
これでnotebookだけ変更すれば他の全てのファイルも同期されるようになります!
↓このリポジトリは完全にこのような方法で開発しているので参考にしてください
https://github.com/kamata1729/QATM_pytorch