PythonのLOADとSTORE
いわゆる「ローカル変数」の読み込みはLOAD_FAST
= Pythonはデフォルトでローカル変数、global宣言でグローバル変数になる。 JavaScriptはデフォルトでグローバル変数、var宣言でローカル変数になる。 Pythonは代入をするとその関数のローカル変数になるため、 ネストした関数で外の関数のローカル変数を書き換えることができない。
= ささださんに教えてもらったYARVの命令セット。 YARV: Yet another RubyVM / Instruction Table。 やはり同じようにgetlocal, getglobalの他に、getdynamic, getspecialというそれっぽい名前の命令がある。 詳しいことは知らないけど多分にたようなことをするんだろう。
>>> from dis import dis
>>> def foo(x):
x
>>> dis(foo)
2 0 LOAD_FAST 0 (x)
3 POP_TOP
4 LOAD_CONST 0 (None)
7 RETURN_VALUE
グローバル変数はLOAD_GLOBAL
>>> def foo(): x >>> dis(foo) 2 0 LOAD_GLOBAL 0 (x) (以下略)代入があるとローカル変数になる。
>>> def foo():
x = None
>>> dis(foo)
2 0 LOAD_CONST 0 (None)
3 STORE_FAST 0 (x)
global宣言をつけるとグローバルになる。
>>> def foo():
global x
x = None
>>> dis(foo)
3 0 LOAD_CONST 0 (None)
3 STORE_GLOBAL 1 (x)
ここまではとてもわかりやすい。
初級編。
ネストしててもLOAD_GLOBAL
>>> def foo(): def bar(): x >>> dis(foo.func_code.co_consts[1]) 3 0 LOAD_GLOBAL 0 (x)しかし外の関数が同名の変数を定義している場合、LOAD_DEREFになる。
>>> def foo(x): def bar(): x >>> dis(foo.func_code.co_consts[1]) 3 0 LOAD_DEREF 0 (x)ローカル変数への代入自体がSTORE_DEREFに変わっている。
>>> def foo():
x = None
def bar():
x
>>> dis(foo)
2 0 LOAD_CONST 0 (None)
3 STORE_DEREF 0 (x)
中の関数から参照されうるかどうかで判断しているようだ。
>>> def foo():
x = None
def bar():
y
>>> dis(foo)
2 0 LOAD_CONST 0 (None)
3 STORE_FAST 0 (x)
読む命令は書く命令と同じ種類。
>>> def foo():
x = None
x
def bar():
x
>>> dis(foo)
2 0 LOAD_CONST 0 (None)
3 STORE_DEREF 0 (x)
3 6 LOAD_DEREF 0 (x)
ローカル変数は専用の「ローカル変数用のテーブル」に対して行うことで
高速に名前を解決できるようにしている。
外から参照されうる変数は別のテーブルに保管する。
どこに保管するのかはコンパイル時に決定してそれぞれ異なるバイトコードで表現されている。 ではコンパイル時に決定できないようにしたらどうなるのか。
>>> def foo():
exec "x = None"
x
>>> dis(foo)
2 0 LOAD_CONST 1 ('x = None')
3 LOAD_CONST 0 (None)
6 DUP_TOP
7 EXEC_STMT
3 8 LOAD_NAME 0 (x)
LOAD_NAMEになる。おそらく理屈通りに順番に探していく命令。
>>> def foo(): exec "x = None" def bar(): x SyntaxError: unqualified exec is not allowed in function 'foo' it contains a nested function with free variables (これはシンタックスエラー, line 2)
>>> def foo():
exec "x = None" in locals()
def bar():
x
>>> dis(foo.func_code.co_consts[2])
4 0 LOAD_GLOBAL 0 (x)
3 POP_TOP
4 LOAD_CONST 0 (None)
7 RETURN_VALUE
localsに限定してexecした場合でも、中の関数はLOAD_GLOBALになる。
execによって導入される変数は同一関数以外からは見えないのか?
= Pythonはデフォルトでローカル変数、global宣言でグローバル変数になる。 JavaScriptはデフォルトでグローバル変数、var宣言でローカル変数になる。 Pythonは代入をするとその関数のローカル変数になるため、 ネストした関数で外の関数のローカル変数を書き換えることができない。
>>> def make_counter(): count = 0 def push(): count += 1 # ダメ return count; return pushJavaScriptはvarがあるおかげで、これができる。
function make_counter(){
var count = 0;
function push(){
return ++count;
}
return push;
}
しかし、これはクラス使いの立場から見ると
「メソッドを一つだけ持つようなオブジェクトが簡単に作れるというだけ」
となる。
>>> class Counter(): def __init__(self): self.count = 0 def __call__(self): self.count += 1 return count別に行数変わらないし、今後メソッドが増えた場合にJSのクロージャ的アプローチでは 何らかの形で複数の関数オブジェクトをまとめなければいけなくなって複雑になる。 プロトタイプ使うのかな?
= ささださんに教えてもらったYARVの命令セット。 YARV: Yet another RubyVM / Instruction Table。 やはり同じようにgetlocal, getglobalの他に、getdynamic, getspecialというそれっぽい名前の命令がある。 詳しいことは知らないけど多分にたようなことをするんだろう。