jjlin
jjlin

Reputation: 4703

Is it possible to get formal argument names/values as a dictionary?

If I have a Python function defined as f(a, b, c), is there a straightforward way to get a dictionary mapping the formal names to the values passed in? That is, from inside f, I'd like to be able to get a dictionary {'a': 1, 'b': 2, 'c': 3} for the call f(1, 2, 3).

I'd like to do this so I can directly use the arguments and values in a string substitution, e.g. "%(a)s %(b)s %(c)s" % d. I could just do something like d = dict(a=a, b=b, c=c), but I'd rather avoid the repetition if possible.

I know this is quite easy to do by defining f as f(**kwds), but that also makes it less obvious what arguments the function expects. It looks like there'd probably be some way to do this via the inspect module, but it'd probably be too complex to be worthwhile.

I suspect there's no good answer to this question as posed, but I'd be happy to be proven wrong. Alternative approaches for accomplishing what I described above would be welcome too.

Upvotes: 5

Views: 257

Answers (4)

wim
wim

Reputation: 362836

>>> import inspect
>>> def foo(a,b,c):
...     pass
... 
>>> inspect.getargspec(foo)
ArgSpec(args=['a', 'b', 'c'], varargs=None, keywords=None, defaults=None)
>>> inspect.getargspec(foo).args
['a', 'b', 'c']

Now you can filter locals() based on those.

>>> x = 'outer'
>>> G = 'GLOBAL'
>>> def foo(a, b, c=1, *splat, **splatsplat):
...     global G
...     d = 'inner'
...     b = 'stomped'
...     print(locals())
...     print(inspect.getargspec(foo))
...     
>>> foo(1, 2, 3, 'splat1', 'splat2', named='potato')
{'splatsplat': {'named': 'potato'}, 'splat': ('splat1', 'splat2'), 'd': 'inner', 'b': 'stomped', 'c': 3, 'a': 1}
ArgSpec(args=['a', 'b', 'c'], varargs='splat', keywords='splatsplat', defaults=(1,))

The only thing I don't know how to get is to recover the original value of b; I think that's probably impossible.

Upvotes: 7

RemcoGerlich
RemcoGerlich

Reputation: 31260

You could do it with two functions, one with a nice interface and the other with the implementation:

def f(a, b, c):
    """Docstring"""
    return _f(a, b, c)

def _f(**kwargs):
    ...

Upvotes: 1

martineau
martineau

Reputation: 123473

You could avoid even more repetition by instead creating a generic function that does the string substitution as well. The following works in both Python 2.7.6 and 3.3.4:

import inspect
import sys

def print_func_args(call_stack_depth=1):
    frame = sys._getframe(call_stack_depth)
    funcname = inspect.getframeinfo(frame).function
    func = frame.f_globals[funcname]
    f_locals = frame.f_locals
    args = inspect.getargspec(func).args
    print(', '.join(('{}={}'.format(arg, f_locals[arg]) for arg in args)))

def foo(a, b, c):
    local_var = 42
    print_func_args()

foo(1, 2, 3)  # -> a=1, b=2, c=3

Upvotes: 0

anon582847382
anon582847382

Reputation: 20371

You can use locals() for the current scope:

>>> def f(a, b, c):
...     param_values = locals()  # Line must be very first in function.
...     print(param_values)

>>> f(1, 2, 3)
{'a': 1, 'b': 2, 'c': 3}

Conveniently, this seems to be the exact output that you want. In my opinion, this is the best way to do it from inside the function as you specified.

Upvotes: 3

Related Questions