localsに関して誤解していた件

Python2.6とかで

>>> def foo():
...   locals()["hoge"] = 1
...   print hoge
... 
>>> foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in foo
NameError: global name 'hoge' is not defined

こうなるわけで、これはローカルの名前空間が最適化の関係で書き換え不可になっていてlocals()が返すのはそのコピーを辞書ライクにしたものだと思ってたんだ。

でもこれってコンパイル時にhogeが存在しないからLOAD_GLOBALになっているってだけで

>>> import dis
>>> dis.dis(foo)
  2           0 LOAD_CONST               1 (1)
              3 LOAD_GLOBAL              0 (locals)
              6 CALL_FUNCTION            0
              9 LOAD_CONST               2 ('hoge')
             12 STORE_SUBSCR        

  3          13 LOAD_GLOBAL              1 (hoge)
             16 PRINT_ITEM          
             17 PRINT_NEWLINE       
             18 LOAD_CONST               0 (None)
             21 RETURN_VALUE        

こうすればちゃんと書き変わっているのが参照できる。

>>> def foo():
...   locals()["hoge"] = 1
...   print eval("hoge")
... 
>>> foo()
1



「これはローカルの名前空間が最適化の関係で書き換え不可になっていてlocals()が返すのはそのコピーを辞書ライクにしたものだと思ってたんだ。」
これが正解で、evalできるのはlocals()を書き換えたときの動作が未定義だから実装のためにたまたまevalできただけ。

by id:methane