Edited at

Pythonのインタラクティブモードで編集中のモジュールを再読み込みしたい

More than 1 year has passed since last update.


やりたいこと

今回のやりたいことは、表題の通りです。

(淡々と書きますので、ご容赦を...)


なぜやりたいの?

現在、「小さく関数を作ってつなぎ合わせて大きいものを作る」ということを意識して、コツコツと学習をしています。

インタラクティブモードで動作確認をしつつ、エディタ側のソースコードに反映させていくという流れで取り組んでいます。

さて、実際にソースコードの変更をしたので、起動中のpythonのコンソールから、変更の内容、動作確認をしたい...となったとき。

Railsだとconsoleで同じように確認していて、reload! とすると再読み込みしてくれるので、同じようなことがしたい...。

ただしフレームワークを使ったものではなく、単体のスクリプトなので、どうしたものかと調べていました。


とりあえずやってみたこと

こんなコードを用意します。

class Greeting:

def hello(self):
print("Hello World!")

def try_greeting(method_name='hello'):
greeting = Greeting()
if method_name is None:
method_name = 'hello'

eval('greeting.' + method_name)()

def main():
try_greeting()

if __name__== "__main__":
main()

まずは直接pythonコマンドにソースコードを渡して、簡単に動かしてみます。

$ python sample.py 

Hello World!

つぎに -i オプションで、インタラクティブモードで呼び出して動かしてみます。

$ python -i sample.py 

Hello World!
>>> main()
Hello World!

今度は、引数無しで起動します。


>>> import sample
>>> sample.main()
Hello World!

# このあと、エディタ側で print("こんにちは!")に書き換えてsave
# でも変わらない...
>>> sample.main()
Hello World!

# importlibを使って再読み込みさせます

>>> import importlib
>>> importlib.reload(sample)
<module 'sample' from '/Users/akiko/work/convert_qiita/sample.py'>

# こんどは反映されました
>>> sample.main()
こんにちは!


クラスも試します

同じインタラクティブモードのまま。

# 書き換えた状態でGreetingのインスタンスを作成

>>> g = sample.Greeting()
>>> g.hello()
こんにちは!!!!

# このあと、print("こんにちは!!! うまくできてますか?") に書き換え
>>> importlib.reload(sample)
<module 'sample' from '/Users/akiko/work/convert_qiita/sample.py'>

# 再読み込みをさせますが、すでにインスタンス化されているものは、メソッドは変更が反映されません
>>> g.hello()
こんにちは!!!!

# 追加で新しく作ったほうは、変更が反映されています
>>> g2 = sample.Greeting()
>>> g2.hello()
こんにちは!!! うまくできてますか?


まとめ

簡単ですが、うまくいきました。

ただし、上記で試したとおり、クラス利用の場合は挙動に注意。

また、再読み込みする前に生成されたオブジェクトや変数は、変更が適用されていませんので注意。


補足

少し前に、こういうメモを上げましたが、こちらの方法でいけそうかな?