IPythonのモジュール再読み込み

/ IPython Python

IPythonでローカルのモジュールを読み込んだとき、そのモジュールを更新してもIPythonが起動したままだとデフォルトではその更新が反映されない(再読み込みされない)。

  • autoreload
  • reset
  • IPythonの再起動

のどれかが必要になる。基本的にautoreloadでよいがモジュールのどんな変化にでも対応できるわけではないのでその場合は後者2つを選ぶことになる。resetはほとんど再起動と変わらないが入出力のカウンタの番号は継続するという違いがある。

Pythonの別ファイル読み込み

Pythonではプログラムの一部を別ファイルに分割したい場合は、分割した先のプログラムを単体のモジュールとして扱うことになる。Pythonではpipなどでインストールできるライブラリのモジュールをimportで簡単に呼び出すことができるが、ローカルに作った別ファイルのプログラムもこれと同じ枠組みの中で呼び出されることになる。

- utilities.py
- main.py

というファイル構造だったとしてmain.pyからimport utilities as utilとすればutilities.pyが実行されて、その中にあったものにはutil.some_function()という形で呼び出せる。from utilities import some_functionとしておけば接頭辞は不要になる。Pythonはローカルなモジュールであってもutilities.pyのタイムスタンプをチェックして必要に応じてキャッシュを作成する。

- __pycache__
 + - utilities.cpython-38.pyc
- utilities.py
- main.py

というようにサブフォルダにPython処理系のバージョンごとにキャッシュを作ってくれる。必要に応じてとは具体的には.pyの更新時刻が対応する.pycの更新時刻よりも新しい場合。.pycが更新不要ならばコンパイル済のバイトコードを対応する.pycから読み込むことでインポートが高速化される。

重複インポート時の挙動

問題はIPython内でrun main.pyから間接的にimport utilitiesを実行したときの挙動。Pythonでは複数回同じファイルをインポートしても既にインポート済ならば再読み込みを行わない。それ自体1つのPythonプログラムであるIPython上でも複数回同じファイルがインポートされても2回目以降は何もしないしキャッシュの更新もしない。

これはIPython上で実際にimport、.pyの更新、importを行うことによって確かめることが出来る。

In [1]: import utilities

-> ここでutilities.pyを更新

In [2]: import utilities

-> utilities.cpython-38.pycは更新されない

これはある意味普通のことで、なぜならもし根本的にutilities.pyに関係するものを完全に新しいutilities.pyのものに変えてしまうと1行目と2行目の間に、古いutilities.pyにしかないクラスのインスタンスがあった場合に、それらのインスタンスの変数やメソッドが消えてしまうという不都合が起きるから。

自分的にはutilities.pyの変更がマイナーだったとしてもPython側からはその変更がどの程度か予想できない。

IPythonモジュールのautoreloadを使う

もし再読み込みするとしても古いutilities.pyの内容を保持したまま必要なものを上書きする形で再読み込みをするのであれば問題は無いはずで、断続的にマイナーな変更を加えるようなよくあるケースでは好ましい動作である。そのような形の再読み込みを自動的に行ってくれるのがIPython拡張モジュールのautoreloadで、特に何もしなくても元から入っている。

https://ipython.readthedocs.io/en/stable/config/extensions/autoreload.html

In [1]: load_ext autoreload

In [2]: autoreload 2

としておけば何かのPythonコードを実行するたびにutilities.pyの内容がワークスペースを上書きする形で再読み込みされる。基本的にはほとんどのモジュールがキャッシュヒットするが.pyが更新されていれば新しい内容が読み込まれて.pycが作り直される。

ほとんど問題ないが上記ページでもようにどんな改変に対しても正しく再読み込みをするのは容易なことではないので定期的にIPythonそのものを再起動してやるほうがいい。

バージョンメモ

Python 3.8.2 (tags/v3.8.2:7b3ab59, Feb 25 2020, 23:03:10) [MSC v.1916 64 bit (AMD64)]
Type 'copyright', 'credits' or 'license' for more information
IPython 7.13.0 -- An enhanced Interactive Python. Type '?' for help.