« find_name_error.py |Main| 台風成果報告会日記 »

« find_name_error.py | Python | »

PythonのLOADとSTORE

いわゆる「ローカル変数」の読み込みはLOAD_FAST
>>> 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 push

JavaScriptは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というそれっぽい名前の命令がある。 詳しいことは知らないけど多分にたようなことをするんだろう。

トラックバック(Trackback)

Trackback URL: http://www.nishiohirokazu.org/mt/mt-tb.cgi/643

ご意見・ご感想をお送りください(フィードバック)

(フィードバックはメールで送信され、基本的に表示されませんが、内容によっては公開させていただくこともございます。ご了承ください。Your comment doesn't appear the page immediately. If the comment has value to other people, it will be put on the page or subsequent entries. Thank you.)

上の情報は、いずれも未記入でかまいません。 All of above questions are optional.