「」に属する記事(最新5件のみ展開表示)

メイン

2007年09月07日

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

2007年09月06日

find_name_error.py

ドキュメントはまだありません。(たぶん月曜日以降になる)

ファイルをダウンロード

from foo import *で読み込まれた名前は感知できません、ので警告が出ます。 foo.barでbarが存在しない可能性は感知できません。関数のネストには対応しています。LOAD_DEREFなんてバイトコード! 雨がひどくなってきてしまった。ひどくなる前に変えるつもりだったのに。むしろこの雨の中帰りたくないけど帰らないとしんどいなぁ。 続きは来週。

C:\cur\misc\coda>python find_name_error.py -r C:\cur\doukaku_proj
*** manage.py
line 7: __file__
*** stat.py
line 13: models
line 14: models
*** urls.py
line 3: patterns
line 5: include
line 7: include
*** generate_css.py
line 5: out
*** test_maker.py
line 38: f
line 39: f
line 40: f
line 41: f
line 42: f
line 45: f
line 46: f
line 50: f
line 51: f
line 52: f
line 53: f
line 65: f
line 66: f
line 67: f
line 79: f
line 17: f
line 18: f
line 19: f
line 20: f
line 21: f
line 22: f
line 23: f
line 27: f
line 33: f
line 73: f
line 74: f
line 75: f
line 76: f
line 25: patterns
line 45: patterns
line 139: include
line 143: patterns
line 177: patterns
*** apply.py
line 46: WithRatingNode
line 46: var
*** with_rating.py
line 46: TemplateSyntaxError
invalid syntax (profile.py, line 16)
*** search.py
line 40: render_to_response
line 98: render_to_response
*** xml.py
line 42: render_to_response
line 11: render_to_response
*** __init__.py
line 309: models
line 313: models
line 103: models
line 374: render_to_response
line 330: models
line 30: models
line 31: models
line 32: models
line 33: render_to_response
line 256: models
line 262: render_to_response
line 266: render_to_response
line 271: models
line 274: models
line 286: models
line 69: models
line 72: render_to_response
line 125: models
line 129: models
line 131: render_to_response
line 382: render_to_response
line 386: models
line 150: models
line 138: models
line 139: models
line 141: render_to_response
line 302: models
line 219: models
line 239: render_to_response
line 197: models
line 181: models
line 42: models
line 43: models
line 80: models
*** unistring.py
line 91: __file__
line 105: __file__
*** _mapping.py
line 44: __file__
line 64: __file__
line 73: __file__
*** _luabuiltins.py
line 253: __file__
line 107: __file__
line 126: __file__
line 135: __file__
*** _phpbuiltins.py
line 3376: __file__
line 3385: __file__
*** forms.py
line 51: _
line 56: _
line 75: _
line 66: _
*** models.py
line 116: _
line 15: patterns
line 20: url
line 23: url
line 27: url
line 31: url
line 34: url
line 37: url
line 40: url
line 43: url
line 46: url
追記
def foo():
    sleep(2**100)
    print x
を実行することなく「xがNameError起こしそう」と判定してくれる、Perlで言うところの-cwでっす。

2007年04月04日

Pythonで日本語PDFをつくるサンプル

西尾泰和のブログ @ Cybozu Labs: Pythonで日本語PDFをつくる短いサンプルに移動しました。

2007年03月30日

immutableクイズ

空欄を埋めよ。
>>> dic = {}
(空欄)
>>> dic
{[1]: 1, [2]: 2, [3]: 3}
>>> for k in dic.keys():
	k.append(0)

	
>>> dic
{[1, 0]: 1, [2, 0]: 2, [3, 0]: 3}

答えは2通り以上あるけど、、どういうのとどういうの、っていうとヒントになるから言わない。

ファインマンが 「小学校の教科書で『電車はなぜ動くのでしょう?それはエネルギーが動かすからです』なんて説明はまずくないか?それを習った生徒が『エネルギー』という言葉なしで習ったことを説明できるか?できないのは、結局言葉の定義以外を覚えただけだからじゃないのか?」というようなことを言っていた。 で、(im)mutableという言葉を使わずになぜPythonの辞書にはリストが入れられないのかを説明できるだろうか?

初心者向けの解説で「リストはミュータブル(変更できる)ので辞書には入れられません」なんて説明をするのはやめたほうがいいのではないだろうか。

答えは30cm下で書く。

>>> dic = {}
>>> class Foo(object):
	def __init__(self, vs):
		self.value = vs
	def __repr__(self):
		return repr(self.value)
	def append(self, v):
		self.value.append(v)

		
>>> dic[Foo([1])] = 1
>>> dic
{[1]: 1}
>>> class Bar(list):
	def __hash__(self):
		return id(self)

	
>>> dic[Bar([2])] = 2
>>> dic
{[1]: 1, [2]: 2}
>>> class Baz(list):
	def __hash__(self):
		return sum(self)

	
>>> dic[Baz([3])] = 3
>>> dic
{[1]: 1, [2]: 2, [3]: 3}
>>> for k in dic.keys():
	k.append(0)

	
>>> dic
{[1, 0]: 1, [2, 0]: 2, [3, 0]: 3}
このように、辞書にリストを入れること自体は別に難しいことではない。 ではなぜ、わざわざ辞書にリストが入らないようにしてあるのだろうか?

id(オブジェクトの同一性)を使って区別をするFooとBarの場合、下のような現象が起きる。

>>> dic2 = {}
>>> dic2[Bar([])] = 1
>>> dic2[Bar([])] = 2
>>> dic2[Bar([1])] = 3
>>> dic2[Bar([1])] = 4
>>> dic2
{[]: 1, []: 2, [1]: 4, [1]: 3}
1つ目の[]と2つ目の[]は別物だから。 下のコードはエラーになる。
>>> dic = {}
>>> dic[Bar([])] = 1
>>> dic
{[]: 1}
>>> k = Bar([])
>>> k
[]
>>> print dic[k]

Traceback (most recent call last):
  File "", line 1, in -toplevel-
    print dic[k]
KeyError: [] # 1つ目の[]と2つ目の[]は別物なのでキーが見つからない

中身を見て区別をする(同じ中身のものは同じと見なす)ようにすると、 この現象は起こらなくなる。

>>> dic = {}
>>> dic[Baz([1, 2])] = 1
>>> dic
{[1, 2]: 1}
>>> k = Baz([1, 2])
>>> k
[1, 2]
>>> print dic[k]
1
しかし、中身で分類しているので、中身を書き換えられると困る。 中身の合計が3だから3の棚にしまったので 6の棚を探しても見つからない。
>>> k = dic.keys()[0]
>>> k
[1, 2]
>>> k.append(3)
>>> k
[1, 2, 3]
>>> dic
{[1, 2, 3]: 1}
>>> print dic[Baz([1, 2, 3])]

Traceback (most recent call last):
  File "", line 1, in -toplevel-
    print dic[Baz([1, 2, 3])]
KeyError: [1, 2, 3]
こうなったら書き換えられないリストを作るしかないんじゃない? というわけでタプルが生まれたのではないかと思う。

2007年03月22日

Haskell In Python ver. 0.01

Haskellのコードを呼び出すための Pythonモジュールを作ってみました。

使い方。

>>> from Haskell import Haskell
>>> h = Haskell("""
func = reverse
func' x = map (+ x) 
value = 42
""")
>>> h.func
<Haskell.HaskellFunc object at 0x0153AC90>
>>> range(5)
[0, 1, 2, 3, 4]
>>> h.func(range(5))
[4, 3, 2, 1, 0]
>>> type(_)
<type 'list'>
>>> func2 = h.get("func'")
>>> func2(1, range(5))
[1, 2, 3, 4, 5]
>>> h.value
<Haskell.HaskellFunc object at 0x0153AD90>
>>> h.value()
42
>>> type(_)
<type 'int'>

仕組み。

PythonからHaskellを呼ぶためには、

  • 1:PythonのオブジェクトをなんらかのHaskellが扱える形に変換
  • 2:変換したモノを何らかの方法でHaskellに渡す
  • 3:Haskellが渡されたモノをHaskellのオブジェクトに変換
  • 4:Haskellが処理
  • 5:処理の結果のHaskellオブジェクトをPythonが扱える形に変換
  • 6:変換したモノを何らかの方法でPythonに渡す
  • 7:Pythonが渡されたモノをPythonのオブジェクトに変換
という手順を踏みます。

このバージョン0.01では

  • 1:Pythonのオブジェクトをstrを使って文字列に変換
  • 2:変換した文字列をソースコードに埋め込んでHaskellを実行
  • 3:Haskellパーサが文字列をパースしてHaskellのオブジェクトを作る
  • 4:Haskellが処理
  • 5:処理の結果のHaskellオブジェクトをshowで文字列に変換する
  • 6:文字列を標準出標準出力に力に吐いて、Pythonがそれを拾う
  • 7:Pythonが文字列をevalしてPythonのオブジェクトに変換
という感じになっています。 変換処理をそれぞれの言語の処理系に丸投げしたので40行程度でかけました。
= 既知の問題点。

  • 5番の変換にshowを使っているのでShowのインスタンスでないとPython側に渡せない。 また、Showのインスタンスであって、Python側に渡せたとしても7番の変換でこけるケースがある。
    • 例えば関数はShowのインスタンスでないので渡せない
    • 例えば「Just 1」はShowのインスタンスなので渡せるけどevalできない
    • 解決法:
      • たとえばPythonizableというクラスを作って、Intなどをそのインスタンスにする。
      • toPython :: (Pythonizable a) => a -> String を作る
  • HaskellでPythonの関数をpureな関数として扱えない
    • Pythonと通信するためにIOモナドになってしまうから
    • Haskellのコードで記述するのではなく、なにか拡張を作ってHaskell処理系をだましてやる必要がある

=
'''
  Haskell In Python

USAGE:
code = """
func = reverse
func' x = map (+ x) 
value = 42
"""

h = Haskell(code)

print h.func(range(5))
print h.get("func'")(1, range(5))
print h.value()
'''
__all__ = ["Haskell"]
class Haskell(object):
    def __init__(self, code):
        self.code = code
        
    def __getattr__(self, name):
        return HaskellFunc(self.code, name)
    def get(self, name):
        return HaskellFunc(self.code, name)


class HaskellFunc(object):
    def __init__(self, code, name):
        self.code = code
        self.name = name
    def __call__(self, *args):
        argsStr = " ".join(str(arg) for arg in args)
        f = file("tmp.hs", "w")
        print >>f, self.code
        print >>f, "main = print $ %s %s" % (self.name, argsStr)
        f.close()
        from popen2 import popen3
        (o, i, e) = popen3("runghc tmp.hs")
        err = e.read()
        if err:
            raise RuntimeError(err)
        return haskellStr2pythonObj(o.read())


def haskellStr2pythonObj(s):
    return eval(s)

古い記事タイトル一覧

凡例{ ●: 単一エントリーへのリンク, □: そこから最新記事までを一覧表示, ■: そこから最新記事までをwindow.openで開く}(comming soon)

2007年03月08日
 ■ □ By 西尾泰和 at 2007-03-08 10:22:53
2007年02月22日
 ■ □ By 西尾泰和 at 2007-02-22 22:35:42
2007年02月15日
 ■ □ By 西尾泰和 at 2007-02-15 23:09:33
2007年02月13日
 ■ □ By 西尾泰和 at 2007-02-13 16:46:06
2007年01月21日
 ■ □ By 西尾泰和 at 2007-01-21 02:18:38
2007年01月18日
 ■ □ By 西尾泰和 at 2007-01-18 17:24:58
 ■ □ By 西尾泰和 at 2007-01-18 16:57:23
2007年01月17日
 ■ □ By 西尾泰和 at 2007-01-17 21:35:45
2006年12月27日
 ■ □ By 西尾泰和 at 2006-12-27 07:12:33
2006年11月29日
 ■ □ By 西尾泰和 at 2006-11-29 17:07:26
2006年11月23日
 ■ □ By 西尾泰和 at 2006-11-23 01:24:11
2006年11月20日
 ■ □ By 西尾泰和 at 2006-11-20 18:46:36
2006年11月06日
 ■ □ By 西尾泰和 at 2006-11-06 00:44:25
2006年11月05日
 ■ □ By 西尾泰和 at 2006-11-05 02:40:01
2006年10月24日
 ■ □ By 西尾泰和 at 2006-10-24 00:16:13
2006年10月12日
 ■ □ By 西尾泰和 at 2006-10-12 09:43:12
 ■ □ By 西尾泰和 at 2006-10-12 04:18:58
2006年10月11日
 ■ □ By 西尾泰和 at 2006-10-11 00:50:09
2006年10月07日
 ■ □ By 西尾泰和 at 2006-10-07 21:19:31
 ■ □ By 西尾泰和 at 2006-10-07 20:47:25
2006年10月04日
 ■ □ By 西尾泰和 at 2006-10-04 15:39:38
2006年09月30日
 ■ □ By 西尾泰和 at 2006-09-30 16:15:22
 ■ □ By 西尾泰和 at 2006-09-30 12:05:19
2006年09月25日
 ■ □ By 西尾泰和 at 2006-09-25 22:51:54
2006年09月24日
 ■ □ By 西尾泰和 at 2006-09-24 00:06:23
2006年09月14日
 ■ □ By 西尾泰和 at 2006-09-14 02:00:01
 ■ □ By 西尾泰和 at 2006-09-14 01:35:32
 ■ □ By 西尾泰和 at 2006-09-14 00:18:34
 ■ □ By 西尾泰和 at 2006-09-14 00:16:33
2006年09月08日
 ■ □ By 西尾泰和 at 2006-09-08 18:22:49
2006年09月01日
 ■ □ By 西尾泰和 at 2006-09-01 22:17:11
2006年08月22日
 ■ □ By 西尾泰和 at 2006-08-22 02:48:39
2006年08月19日
 ■ □ By 西尾泰和 at 2006-08-19 17:43:10
 ■ □ By 西尾泰和 at 2006-08-19 16:29:22
 ■ □ By 西尾泰和 at 2006-08-19 00:02:10
2006年08月17日
 ■ □ By 西尾泰和 at 2006-08-17 02:36:51
2006年08月12日
 ■ □ By 西尾泰和 at 2006-08-12 18:23:37
2006年08月09日
 ■ □ By 西尾泰和 at 2006-08-09 19:24:17
2006年07月29日
 ■ □ By 西尾泰和 at 2006-07-29 01:59:08
2006年07月27日
 ■ □ By 西尾泰和 at 2006-07-27 16:10:50
 ■ □ By 西尾泰和 at 2006-07-27 13:13:48
 ■ □ By 西尾泰和 at 2006-07-27 12:59:04
2006年07月26日
 ■ □ By 西尾泰和 at 2006-07-26 18:45:06
2006年07月17日
 ■ □ By 西尾泰和 at 2006-07-17 19:18:49
 ■ □ By 西尾泰和 at 2006-07-17 17:46:51
2006年07月15日
 ■ □ By 西尾泰和 at 2006-07-15 13:54:00
2006年07月14日
 ■ □ By 西尾泰和 at 2006-07-14 09:30:52
2006年07月13日
 ■ □ By 西尾泰和 at 2006-07-13 13:46:01
 ■ □ By 西尾泰和 at 2006-07-13 02:58:53
2006年07月10日
 ■ □ By 西尾泰和 at 2006-07-10 18:16:16
2006年07月06日
 ■ □ By 西尾泰和 at 2006-07-06 01:56:34
2006年07月03日
 ■ □ By 西尾泰和 at 2006-07-03 02:50:24
2006年07月02日
 ■ □ By 西尾泰和 at 2006-07-02 18:40:44
2006年06月26日
 ■ □ By 西尾泰和 at 2006-06-26 23:41:15
2006年06月24日
 ■ □ By 西尾泰和 at 2006-06-24 12:44:54
2006年06月03日
 ■ □ By 西尾泰和 at 2006-06-03 01:46:52
2006年06月01日
 ■ □ By 西尾泰和 at 2006-06-01 01:20:52
2006年05月30日
 ■ □ By 西尾泰和 at 2006-05-30 17:03:02
 ■ □ By 西尾泰和 at 2006-05-30 15:42:22
2006年05月27日
 ■ □ By 西尾泰和 at 2006-05-27 22:56:48
2006年05月26日
 ■ □ By 西尾泰和 at 2006-05-26 14:21:38
2006年05月17日
 ■ □ By 西尾泰和 at 2006-05-17 20:48:42
 ■ □ By 西尾泰和 at 2006-05-17 16:09:13
2006年05月15日
 ■ □ By 西尾泰和 at 2006-05-15 23:52:42
2006年05月11日
 ■ □ By 西尾泰和 at 2006-05-11 17:18:36
 ■ □ By 西尾泰和 at 2006-05-11 15:05:21
 ■ □ By 西尾泰和 at 2006-05-11 14:56:43
2006年05月08日
 ■ □ By 西尾泰和 at 2006-05-08 19:08:14
2006年04月22日
 ■ □ By 西尾泰和 at 2006-04-22 14:27:44
2006年04月06日
 ■ □ By 西尾泰和 at 2006-04-06 10:46:33
2006年03月31日
 ■ □ By 西尾泰和 at 2006-03-31 12:31:06
2006年03月30日
 ■ □ By 西尾泰和 at 2006-03-30 08:35:36