how to get parameter decl of a function?
Alex Martelli
alex at magenta.com
Mon Aug 28 07:23:37 EDT 2000
"Robert Kiendl" <rkiendl at gmx.net> wrote in message
news:39AA3F24.BF4EC75 at gmx.net...
> is it possible to get the declaration of the the argument-list of any
> function in the python engine? just like it is possible to get the
> func_name or doc-string.
Yes; a very good packaging of this you can find in Lemburg's module 'hack',
see http://starship.python.net/crew/lemburg/hack.py.
Don't get put off by the module's name -- it's just a collection of
"tools that I find useful to examine code from inside an interactive
interpreter session", as the author says!
The functionality you want is packaged as part of function
func_sig of this module.
It is, of course, instructive to examine exactly what this function
does. First of all:
def func_sig(func):
if hasattr(func,'im_func'):
func = func.im_func
This takes care of the case where 'func' is not really a function, but
rether a method; in this case, the real function is its attribute im_func,
so this part "normalizes" this case back to the general case. Then:
code = func.func_code
fname = code.co_name
callargs = code.co_argcount
args = code.co_varnames[:callargs]
Now we have the function name (fname), and the arguments' names (args).
With just this part, we can already, in a sense, satisfy your request:
def func_args(func):
if hasattr(func,'im_func'):
func = func.im_func
code = func.func_code
fname = code.co_name
callargs = code.co_argcount
args = code.co_varnames[:callargs]
return "%s(%s)" % (fname, string.join(args,','))
and now for example:
>>> fa.func_args(string.join)
'join(words,sep)'
>>>
What we're still missing are the default values of arguments that do
have them -- here, for example, the fact that the 'sep' argument can
be omitted, because it defaults to space. And also, the ability of
some functions to accept unlimited lists of arguments (the *args form,
and/or **kw for keyword-arguments). Lemburg goes into slightly deeper
waters to add this information, too...:
args = list(code.co_varnames[:callargs])
...list, not tuple, because we may want to change some elements...
if func.func_defaults:
i = len(args) - len(func.func_defaults)
for default in func.func_defaults:
try:
r = repr(default)
except:
r = '<repr-error>'
if len(r) > 100:
r = r[:100] + '...'
...repr is used to get the string with which to represent the
default value, with due care for possible errors, and/or
too-long representations, which get truncated to 100 chars
(with an ellipsis ... to remind the reader they're truncated)
arg = args[i]
if arg[0] == '.':
# anonymous arguments
arg = '(...)'
args[i] = '%s=%s' % (arg,r)
i = i + 1
I don't know what "anonymous arguments" are -- I'm learning of
their very existence by studying this code (talk about instructive!-)
if code.co_flags & 0x0004: # CO_VARARGS
args.append('*'+code.co_varnames[callargs])
callargs = callargs + 1
if code.co_flags & 0x0008: # CO_VARKEYWORDS
args.append('**'+code.co_varnames[callargs])
callargs = callargs + 1
As Lemburg notes in his comments, these constants are taken right
from the Python compiler's include file.
return '%s(%s)' % (fname,string.join(args,', '))
At last -- the nicely formatted result!
>>> func_sig(string.join)
"join(words, sep=' ')"
>>>
Just one caveat: this will not work for functions written in C!
The "signature" is not available in this case by "introspection"
in this way; to see the difference:
>>> import string
>>> import strop
>>> dir(string.join)
['__doc__', '__name__', 'func_code', 'func_defaults', 'func_doc',
'func_globals', 'func_name']
>>> dir(strop.join)
['__doc__', '__name__', '__self__']
>>>
i.e., the Python-written 'string.join' wrapper offers the various
func_code, etc, members upon which this introspection can be
based; the bare-C 'strop.join' underlying level doesn't, so,
no real introspection -- you have to rely on the __doc__:
>>> print strop.join.__doc__
join(list [,sep]) -> string
joinfields(list [,sep]) -> string
Return a string composed of the words in list, with
intervening occurences of sep. Sep defaults to a single
space.
(join and joinfields are synonymous)
>>>
Not too bad, after all:-).
Alex
More information about the Python-list
mailing list