関数に与えられた * 引数、 ** 引数の名前も調べる
関数に与えられた引数の名前を調べる を読んで。 func_code の存在を思い出していました。そして、この方法だと * 引数、 ** 引数の名前が取り出せないので追加を。
inspect モジュールの出番
結論から。inspect.getargspec で得られます。行われているのは func_code らのより丁寧な調査です。 inspect.getargspec を使うとデフォルト値まで取り出せるおまけがついてきます。
>>> def sample2(self, arg0, arg1=None, *args, **kwargs): ... local0 = None ... local1 = None ... abcdef = None ... >>> import inspect >>> inspect.getargspec(sample2) (['self', 'arg0', 'arg1'], 'args', 'kwargs', (None,))
そして、 alisue さんの危惧していた func_code.co_varnames の内容の順序ですが、これは inspect モジュールの実装から推測するに、引数、* 引数、 ** 引数、ローカル変数の順で並んでいるようです。心配ありません。
inspect モジュール、その中身は?
inspect モジュールでは何が行われているのか、踏み込んでみます。 Python 2.5.4 の inspect.getargspec のコードです。
def getargspec(func): """Get the names and default values of a function's arguments. A tuple of four things is returned: (args, varargs, varkw, defaults). 'args' is a list of the argument names (it may contain nested lists). 'varargs' and 'varkw' are the names of the * and ** arguments or None. 'defaults' is an n-tuple of the default values of the last n arguments. """ if ismethod(func): func = func.im_func if not isfunction(func): raise TypeError('arg is not a Python function') args, varargs, varkw = getargs(func.func_code) return args, varargs, varkw, func.func_defaults
まずメソッドであるかどうか確認し、メソッドであった場合は解析対象を im_func に変更しています。次に関数であるか確認しています。そして func_code を対象に inspect.getargs 関数を実行しています。これで引数名、 *引数名、 ** 引数名が得られます。最後にデフォルト値を func_defaults から得ています。
続きまして、 Python 2.5.4 の inspect.getargs のコードです。
# These constants are from Python's compile.h. CO_OPTIMIZED, CO_NEWLOCALS, CO_VARARGS, CO_VARKEYWORDS = 1, 2, 4, 8 def getargs(co): """Get information about the arguments accepted by a code object. Three things are returned: (args, varargs, varkw), where 'args' is a list of argument names (possibly containing nested lists), and 'varargs' and 'varkw' are the names of the * and ** arguments or None.""" if not iscode(co): raise TypeError('arg is not a code object') nargs = co.co_argcount names = co.co_varnames args = list(names[:nargs]) step = 0 # 中略 varargs = None if co.co_flags & CO_VARARGS: varargs = co.co_varnames[nargs] nargs = nargs + 1 varkw = None if co.co_flags & CO_VARKEYWORDS: varkw = co.co_varnames[nargs] return args, varargs, varkw
co は inspect.getargspec から来るので func_code であることが期待されます。まずは co がコードオブジェクトかどうか確認しています。
その後は引数名を得ています。引数名は co_varnames リストから先頭 co_argcount の数だけ要素を取り出すことで得られます。
次に * 引数名、 ** 引数名ですが、まず存在するかどうか調べないといけません。存在するならば、これらの名前は co_varnames の引数、ローカル変数の間に挟まっています。これは co_flags のあるビットがたっているかどうか調べることで確認できます。
中略部分は anonymous (tuple) arguments なるものに対応するための 33 行のコードです。これは co_code バイトデータを直接覗き込んでいるコードで、よくわかりませんでした。私の理解としては「まれに co_varnames に入っている名前が引数名でないことがあり、この通りにすると正しい引数名が取り出せる」らしいというところまでです。興味がわいてきた方はお手元の inspect.py ソースコードを読んでみてください。
ちなみに、この中略部分は Python 3.0.1 の inspect.py には存在しませんでした。バッドノウハウの類なのかもしれません。