あけましておめでとうございます。
新年早々ですが、先生、みなさんに言いたいことがあります。
pythonコードで、if __name__ == '__main__'の下にコードをダラダラと書く人、挙手しなさい。
こんな感じに、if __name__ == '__main__'の下にコードを書く人です。
#! usr/bin/python
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, print_function, absolute_import
import sys
def say_args(kind, data_list):
print("kind:", kind, " data:", data_list)
if __name__ == '__main__':
args = sys.argv[1:]
assert args
kind = args[0].lower()
data_list = [x.lower() for x in args[1:]]
say_args(kind, data_list)
こういうコードを書いている人、先生、怒らないから挙手しなさい。
こういうコードはグローバルスコープの名前空間を汚染しているという事に気付いていますか?
気付いており理解した上で書いているという人、手を下ろして結構です。
if __name__ == '__main__'下はグローバルスコープ
そうです、if __name__ == '__main__'下はグローバルスコープです。
そこで定義された変数は、すべてグローバル変数になるということです。
先ほどのコードではローカル変数のつもりのargs
, kind
, data_list
、がグローバル変数になってしまうのです。
ですので、以下のようなコードを書いてもエラーにはなりません。
def say_args(kind, data_list):
print("kind:", kind, " data:", data_list)
# 未定義のargsを参照してもエラーにならない、グローバル変数argsが参照される
print("args: ", args)
※上記の一文は修正しています、詳しくは「2017-01-02 変数「x」について訂正」をご覧ください。
これは直接実行するとエラーにならず、モジュールとして呼ぶとName Errorになるという、やっかいなバグの元になります。
そこ、python先生の言語仕様をディスるのはやめなさい。
この件についてはPyCharm先生も「Shadows name 'kind' form outer scope」とお怒りです。
解決方法
if __name__ == '__main__'下でコードを書きたい場合は、関数でくるみましょう。
#! usr/bin/python
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, print_function, absolute_import
import sys
def main(args):
assert args
kind = args[0].lower()
data_list = [x.lower() for x in args[1:]]
say_args(kind, data_list)
def say_args(kind, data_list):
print("kind:", kind, " data:", data_list)
if __name__ == '__main__':
main(sys.argv[1:])
main関数でくるむことによってグローバルスコープの名前空間は汚染されません。またmain関数でくるむことにより、main処理を冒頭に書けるのでコードの見通しがよくなります。
python初心者のみなさんは関数でくるんでおいた方が無難です。
他にもいろいろ心配という方はPyCharm先生を使ってください、それはもう小姑のように・・いえ、手取り足取りコードのダメな所を指摘してくれます。
pyflask先生 pyflakes先生やpylint先生でもかまいません、pylint先生の方が厳しめです。
(1/1 修正:pyflask先生はいません、pyflakes先生の間違いでした)
先生の言いたかったことは以上です。
それから「main関数でくるんだら、モジュールとして使いたい時に無駄なmain関数ができるじゃん」という人、後で職員室で先生と個別に話し合いましょう。
補講を書きました
【補講】if __name__ == '__main__' の下にコードをダラダラと書く人、挙手しなさい
2017-01-02 変数「x」について訂正
knoguchiさんのコメントを受けまして、訂正いたします。
記事中に訂正を入れると記事が読みにくくなりますので、文末に移動しています。
[訂正前の文章]
先ほどのコードではローカル変数のつもりのargs
, kind
, data_list
、一時変数のグローバル変数なってしまうのです。x
までも
def say_args(kind, data_list):
print("kind:", kind, " data:", data_list)
# 未定義のxを参照してもエラーにならない、グローバル変数xが参照される
print("x: ", x)
内包表記内で使用されたx
についてはグローバル変数にはなりません、エラーになります。
ただし、PyCharmの実行上はエラーにならないようです、これはPyCharmが高速化のためにグローバルフレームを再利用しているためです。
結果の確認はPyCharm上の実行だけではいけないという事ですね、教訓になりました。
改めてknoguchiさんにお礼を申し上げます。
コメント中の「リストコンプリヘンションの変数リークは再束縛の問題」は内包表記でハマりポイントの1つであるので、コメントも併せてご覧ください。