筆者はPythonの短いプログラムを書く時によくEmacsのPython.elをよく使う。主な理由は長年使っていて手に馴染んでいるからであるが、何よりもプログラムの編集中にカーソルを移動させることなく都度C-c C-cで実行結果を別バッファで見れるのが便利であり、筆者には手放せない。UNIXのシェルからpythonと打って起動する対話モードは、計算機代わりに1行書くのにはよく使うが、2〜3行程度であっても複数行のブロックを書く気になれないし、Jupyter Notebookは起動に時間がかかるし、ブラウザの操作が面倒だし、重い。Emacsはすぐに開くし、Python.elのお陰で、ファイルを開いてコードを書いて、初回はC-c C-p, C-c C-c, C-c C-zで、2回目以降はC-c C-cですぐに実行結果が見れる。
さて、Emacsのpython.elのPython modeでC-c C-cすると if __name__ == '__main__': のブロックが実行されないが、筆者はそれが __name__ == '__main__' じゃないからだと思っていた。
ある日、何か延々と試行錯誤しながらプログラムを作っている途中で、テスト用にC-c C-cで実行する時とシェルから実行する時とで動作を変えたくなって、ふと if __name__ == '__main__': に else: を追加すれば実現できるのではないかと思って、やってみたら成功した。その時、たまたま if False: のブロックが if __name__ == '__main__': の直前にあり、次のような形をしていた。
#!/usr/bin/python
def foo(a, b):
return a + b
if False:
def foo(a, b):
return a - b
if __name__ == '__main__':
print(foo(1, 2))
else:
print(foo(3, 4))
これをC-c C-cで実行すると else: 側が実行されて 7 が出力され、シェルから実行すると if __name__ == '__main__': 側が実行されて 3 が出力される。
グッドアイデアと満足してしばらくそのようにしていた後、 if False: を if True: に変えると、C-c C-cした時に if __name__ == '__main__': 側のコードも else: 側のコードも実行されなくなって、頭の中が???となった。
何が悪いのだろうと思って調べ始め、 if False: に戻して else: 側に入る時に print(__name__) としてみると予想に反して __main__ と表示されるし、 if False のブロックをコメントアウトしてC-c C-cすると
else:
^
SyntaxError: invalid syntax
というエラーが出て、混乱した。
if True: にした時に末尾に if __name__ == '__main__': のブロックをもう1つ足すとそちらは実行されたので、それでようやく、C-c C-cでは if __name__ == '__main__': のブロックが消されるのだと気付いた。
C-c C-cで起動されるpython-shell-send-bufferのドキュメントをM-x describe-functionで見ると、
と書いてあるだけで、どういう仕組みで"if __name__== '__main__':"のブロックを実行しないようにしているのかが書いていないが、Python.elの中身を追っていくと、途中で実行されるpython-shell-buffer-substringのドキュメントにWhen optional argument SEND-MAIN is non-nil, allow execution of code inside blocks delimited by "if __name__== '__main__':".
1. When optional argument NOMAIN is non-nil everything under an
"if __name__ == '__main__'" block will be removed.
と書いてあった。"if __name__== '__main__':"のブロックを消す具体的な処理を見ると、確かにpython-nav-if-name-mainが if __name__ == (['"])__main__\1: を検索し、python-nav-forward-sexpがブロックの終端まで移動しており、"if __name__== '__main__':"のelse:ブロックは消えないようになっていた。
つまり、直前に if False: ブロックがある時に if __name__ == '__main__': の else: 側のコードが実行されるのは、"if __name__== '__main__':"のブロックが消されて
if False:
def foo(a, b):
return a - b
else:
print(foo(3, 4))
になるからであり、 if True: に変えると実行されなくなるのも、 if False: のブロックをコメントアウトすると else: が SyntaxErrorになるのも当然であった。
残念...
























