日別アーカイブ: 12月 31, 2007

Python:関数の引数あれこれ

[pukiwiki]
PythonでLISPもどきを作る方法を思考実験。((Maximaで遊んでたら、なんとなく、こんなことのやりかたが気になったので。))

そのために必要そうな「あらかじめ引数の数が判ってない関数の呼び出しかた」の答えは、ちゃんとリファレンスマニュアルにありました。

-[[5.3.4 呼び出し (call)(Python リファレンスマニュアル):http://www.python.jp/doc/2.4/ref/calls.html#tok-argument_list]]
-[[3.2 標準型の階層 ->内部型 (internal type) ->コードオブジェクト:http://www.python.jp/doc/2.4/ref/types.html]]

判らなくて、調べるのに結構時間がかかったのでメモ。

。。。って、大晦日になにやってんだか。
—-
Pythonでも、関数名自体が関数へのポインタとして使えます。
_def f1(a,b,c):
_ print a,b,c
という関数があったとき、
_>>> f=f1
_>>> f1(1,2,3)
_1 2 3 #結果

では、変数fに入っている関数が、どのような引数を取るのか判ってない場合はどうしたらいいのか?たとえば、
_f=f1 ; arglist=[1,2,3]

関数のポインタと引数のリストがこのように変数で与えられた時には
_>>> f(*arglist)   #arglistの前にアスタリスク
_1 2 3 #結果
このように呼び出すことができます。”*”がキモです。

あらかじめ、関数定義側での引数名が判っていれば、引数リストを辞書で渡すこともできます
_def f1(a,b,c) : print a,b,c
_
_f=f1 ; d={“a”:1,”b”:2,”c”:3}
_
_f(**d) #  *が二つ
_
_1 2 3 #結果

—-
関数定義側で*や**を使うと、引数の数を自由にとることができます

_def f2(*arglist):
_ s=0
_ for x in arglist :
_ s+=x
_ print s

_>>> f2(1)
1
_>>> f2(1,2,3)
_6
_>>> f2(1,2,3,4,5)
_15
—-
_def f3(**d):
_ for k in d :
_ print k,”=”, d[k],” “,

_>>> f3(a=1,b=2,c=3)
_a = 1 c = 3 b = 2
_>>> f3(hoge=”fuga”)
_hoge = fuga

でもf3を変数名無しで呼び出すと
_>>> f3(1,2,3)
_Traceback (most recent call last):
_ File ““, line 1, in
_ f3(1,2,3)
_TypeError: f3() takes exactly 0 arguments (3 given)

さらにさらにこんなことをすれば、自由自在に引数を取れます。

_def f3(*arglist,**d):
_ for x in arglist :
_ print x
_ for k in d :
_ print k, d[k]

でも、普通に使う分にはIDLEなどで関数の引数の説明などが出なくなるなどのデメリットも。
—-
*関数の引数の数、デフォルト値を調べる
-[[3.2 標準型の階層 :http://www.python.jp/doc/2.4/ref/types.html]]

呼び出し可能型 (callable type) ユーザ定義関数 (user-defined function)より

“def hoge(a,b,c): pass #こんな関数があったら
hoge.func_defaults
#デフォルト値を持つ引数に対するデフォルト値が収められたタプル、デフォルト値を持つ引数がない場合には None

内部型 (internal type) ->コードオブジェクトより

hoge.func_code.co_argcount が固定引数 (positional argument) の個数。

—-
で、これをどういうときに使うのかといいますとー

Pythonのプログラムを、LISP風に構文木で書くときに使えるんじゃないかなーと。

PythonでLISPインタプリタを書くひとは居ない

_max(1,2,3,4,5)

たとえば、最大値を返す関数maxはこんなふうにリストに格納

_L=[max,1,2,3,4,5]
これを呼び出すときは
_>>> L[0](L[1:])
_5 #1~5の最大値は5

—-
またはこんな書き方も?

_L=[max,[1,2,3,4,5],{}]

これをPythonで実行するときは
_>>> L[0](*L[1],**L[2])
_5
—-
sin(x)**2+cos(x)**2は、こんな構文木に。

_from operator import add,mul
_[add,[mul,[sin,x],2.0],[mul,[cos,x],2.0]]

—-
三角関数の加法定理はこちらをカンニングしました。

[[三角関数(ウィキペディア):http://ja.wikipedia.org/wiki/%E4%B8%89%E8%A7%92%E9%96%A2%E6%95%B0#.E4.B8.89.E8.A7.92.E9.96.A2.E6.95.B0.E3.81.AE.E5.8A.A0.E6.B3.95.E5.AE.9A.E7.90.86]]

—-
_>>> from operator import add,mul
_>>> reduce(add,range(3+1))
_6 #1~3の合計
_L=[reduce,add,[range,10]]

構文木は、こうなりますが、実行しようと思うと、range -> reduceの順番に実行して、式を内側から展開する工夫が必要です。
。。。というか、これじゃ

-rangeの呼び出しなのか
-引数としてrangeのポインタを渡しているのか

の区別がつきませんよね。どうしよう?
_class exeObj : pass #マークアップ用オブジェクト
_L=[exeObj,reduce,add,[exeObj,range,10]]

で、リストを内側からwalkしてexeObjが無くなったらパース終了。。。遅そうだな。
_class exeObj:
_ __init__(self,f): self.f=f  #クラスの書き方、まだ憶えてないです。すんません。

_L=[exeObj(reduce),add,[exeObj(range),10]]

こっちのほうがいいかな?

*[[parser — Python解析木にアクセスする:http://www.m-takagi.org/docs/python/lib/module-parser.html]]
“parserモジュールはPythonの内部パーサとバイトコード・コンパイラへのインターフェイスを提供します。このインターフェイスの第一の目的は、PythonコードからPythonの式の解析木を編集したり、これから実行可能なコードを作成したりできるようにすることです。

私の守備範囲を超えております。自分でPythonなLISPインタプリタ書くよりは、バイトコードにコンパイルしてPythonに渡したほうがよさげではあるのですけれども。
*[[Programming/Python/LanguageServices:http://www.hanecci.com/pukiwiki/index.php?Programming%2FPython%2FLanguageServices]]
上記モジュールのサンプルコード
*Pythonで書かれたLISP処理系
メモメモ。

[[λ門 様々な LISP:http://homepage1.nifty.com/shota/lambda/variousLISPs.html]]より

**[[pylisp:http://www.biostat.wisc.edu/~annis/creations/PyLisp/]]

**[[Lisp in Python:http://www.ibiblio.org/obp/py4fun/lisp/lisp.html]]
**[[ Lython: Python用common lispフロントエンド:http://slashdot.jp/developers/04/02/15/1528254.shtml?topic=93]]

*[[最小のLISP(ウィキペディア LISP):http://ja.wikipedia.org/wiki/LISP#.E6.9C.80.E5.B0.8F.E3.81.AELISP]]
“最小のLISPは、以下に示す、Cや機械語で記述された関数だけを必要とする。
他のすべての関数は、効率良くではないが、これらの関数に置き換えて定義できるだろう。

へー。こうなってくると、Pythonにdefuncやlambdaなどに相当する”関数”も欲しいですな。(インチキするとしたら、evalやcompile使うのかしらん?)
*[[Lisp プログラマのための Python 入門:http://www.unixuser.org/~euske/doc/python/python-lisp-j.html]]
>Python にはマクロがない。 ~中略~
>Lisp なら、これと同等の構文木は (+ 2 2) である。この構文木なら誰にでも使えるが、 Python のこんな構文木をいじれるのは本当のエキスパートだけだろう。

Pythonプログラムを、構文木にしてから最適化しよう、という目論見は、私には難しそうです。”いや、最初からできるとは思ってませんでしたけれども。(汗)”

[/pukiwiki]