今さらPython3 (31) - ネームスペースとスコープ
第4章も終盤に差し掛かってきました。今日は、あの謎が解けるかも。
- 作者: Bill Lubanovic,斎藤康毅,長尾高弘
- 出版社/メーカー: オライリージャパン
- 発売日: 2015/12/01
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
変数のスコープ
>>> animal = 'fruitbat' >>> def print_global(): ... print('inside print_global:', animal) ... >>> print('at the top level:', animal) at the top level: fruitbat >>> print_global() inside print_global: fruitbat >>>
globalネームスペースで定義した変数はglobal変数となる。上の例だと、defの外側のglobal領域にanimalという変数を定義して、その変数が関数print_global()の中でも参照されている。実行すると、関数の中で呼んだヤツも'fruitbat'を返していると。
>>> def change_and_print_global(): ... print('inside change_and_print_global:', animal) ... animal = 'wombat' ... print('after the change:', animal) ... >>> change_and_print_global() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in change_and_print_global UnboundLocalError: local variable 'animal' referenced before assignment >>>
今度の例は、change_and_print_global()の1行目でglobal変数としてのanimalをprint()しようとして、その後値を変更、さらに変更後の値をprint()しようとしたけど、エラーが出て怒られたパターン。メッセージを見ると、ローカル変数animalを定義(割当)する前に使われているよと言っている。
>>> def change_local(): ... animal = 'wombat' ... print('inside change local:', animal) ... >>> animal 'fruitbat' >>> change_local() inside change local: wombat >>>
change_local()の中では、animalはグローバル変数ではなくて、ローカル変数として認識されたので、今回はエラーとならなかったと理解すれば良さそう。グローバル変数を照会していなくて、いきなりローカル変数として扱いだしたのでお咎めなし。なるほど。
少し本の例と違ったので、本の通りにやり直し。
>>> def change_local(): ... animal = 'wombat' ... print('inside change_and_print_global:', animal, id(animal)) ... >>> change_local() inside change_and_print_global: wombat 36752224 >>> animal, id(animal) ('fruitbat', 43126280)
id()を使って、同じものか別物かをハッキリさせようという魂胆ですね。分かります。
>>> animal = 'fruitbat' >>> def change_and_print_global(): ... global animal ... animal = 'wombat' ... print('inside change_and_print_global:', animal) ... >>> animal 'fruitbat' >>> change_and_print_global() inside change_and_print_global: wombat >>> animal 'wombat' >>>
animalはグローバル変数だぜぇ。と宣言した後に値を書き換えているので、実行後に改めてanimalの値を呼び出すと上書きされた値になったと。id()使って確認してみる?
>>> animal = 'fruitbat' >>> animal, id(animal) ('fruitbat', 43126280) >>> def change_and_print_global(): ... global animal ... animal = 'wombat' ... print('inside of function:', animal, id(animal)) ... >>> change_and_print_global() inside of function: wombat 36752224 >>> animal 'wombat' >>> id(animal) 36752224
ほう。関数の中でグローバル変数animalのidは書き換わるんだね。それでも、print()したときと処理後に出した値は同じなので、予想通りの結果と言って良いはず。
>>> locals() {'animal': 'wombat', '__spec__': None, '__name__': '__main__', 'change_and_prin _global': <function change_and_print_global at 0x02922030>, '__builtins__': <mo ule 'builtins' (built-in)>, 'square_it': <function square_it at 0x02922108>, 'c oler_add_ints': <function document_it.<locals>.new_function at 0x022F3DB0>, '__ oader__': <class '_frozen_importlib.BuiltinImporter'>, 'document_it': <function document_it at 0x022F3CD8>, 'change_local': <function change_local at 0x022F3F1 >, 'print_global': <function print_global at 0x022F3FA8>, 'add_ints': <function square_it.<locals>.new_function at 0x02922150>, '__package__': None, '__doc__': None, 'add_ins': <function add_ins at 0x022F3E88>} >>> globals() {'animal': 'wombat', '__spec__': None, '__name__': '__main__', 'change_and_prin _global': <function change_and_print_global at 0x02922030>, '__builtins__': <mo ule 'builtins' (built-in)>, 'square_it': <function square_it at 0x02922108>, 'c oler_add_ints': <function document_it.<locals>.new_function at 0x022F3DB0>, '__ oader__': <class '_frozen_importlib.BuiltinImporter'>, 'document_it': <function document_it at 0x022F3CD8>, 'change_local': <function change_local at 0x022F3F1 >, 'print_global': <function print_global at 0x022F3FA8>, 'add_ints': <function square_it.<locals>.new_function at 0x02922150>, '__package__': None, '__doc__': None, 'add_ins': <function add_ins at 0x022F3E88>} >>>
local(), global()で各ネームスペースに保存されている情報が辞書(dict)形式で出力する事が出来るんですね。でも、両方とも同じじゃね?と思ったけど、よく考えたら今いる場所がグローバル領域なので、そこでlocal()すればグローバルそのものがローカルになるという事だと理解。
>>> animal = 'fruitbat' >>> def change_local(): ... animal = 'wombat' #local variable ... print('locals:', locals()) ... >>> animal 'fruitbat' >>> change_local() locals: {'animal': 'wombat'} >>>
・・・local()はこうやって使わないとね。
>>> print('globals:', globals()) #reformatted a little for presentation globals: {'animal': 'fruitbat', '__spec__': None, '__name__': '__main__', 'chang e_and_print_global': <function change_and_print_global at 0x02922030>, '__builti ns__': <module 'builtins' (built-in)>, 'square_it': <function square_it at 0x029 22108>, 'cooler_add_ints': <function document_it.<locals>.new_function at 0x022F 3DB0>, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, 'document_it': <function document_it at 0x022F3CD8>, 'change_local': <function change_local at 0x022F3F60>, 'print_global': <function print_global at 0x022F3FA8>, 'add_ints': <function square_it.<locals>.new_function at 0x02922150>, '__package__': None, '__doc__': None, 'add_ins': <function add_ins at 0x022F3E88>} >>> animal 'fruitbat' >>>
これは、さっき出力してしまったのと同じはず。
_ と __ の疑問を解く
名称の最初と最後が2つのアンダースコアで囲まれたものは、Pythonにあらかじめ予約されています。なぜなら、普通のアプリケーションの開発者が使いそうもないから。
>>> def amazing(): ... ''' This is the amazing function. ... Want to see it again?''' ... print('This function is named:', amazing.__name__) ... print('And its docstring is:', amazing.__doc__) ... >>> amazing() This function is named: amazing And its docstring is: This is the amazing function. Want to see it again? >>>
これ以外には__main__とかもあるよねって、これは知ってるっつーの。アンスコ1個だけのパターンについての解説が欲しかったのに。。。アンスコ1個の時は外から参照しないとか暗黙のルールがあった気がする。
きっとClassのところで解説があるはず!(まだ見てないけど)
(つづく)