Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simulate the assignment of function arguments to *args and **kwargs

Tags:

python

Is there a simple way to "simulate" the assignment of arguments to function parameters?

Say I have a function

def fn(a, b, c='default', *args, **kwargs):
    print(f'a={a}, b={b}, c={c}')
    print(f'args={args}')
    print(f'kwargs={kwargs}')

and I call it the following way:

>>> args = (1, 2, 3, 4)
>>> kwargs = {'extra_kw': 5}
>>> fn(*args, **kwargs)
a=1, b=2, c=3
args=(4,)
kwargs={'extra_kw': 5}

I want to extract what args and kwargs will be in the function fn, without calling fn.

Basically, I am trying to write a function that takes as input a function and a list and dict of arguments and returns the extra args and kwargs:

def extract_extra_args_and_kwargs(f, *args, **kwargs):
    ...

The expected outcome for the example would be:

>>> extract_extra_args_and_kwargs(fn, args, kwargs)
((4,), {'extra_kw': 5})

I have tried playing around with inspect.signature, but given that Python itself must resolve this assignment somewhere I was hoping that I can directly access the corresponding function.

like image 621
jhansen Avatar asked Apr 18 '19 14:04

jhansen


2 Answers

Note that inspect.getcallargs has been deprecated since Python 3.5, per documentation.

Use inspect.signature.bind instead going forward:

from inspect import signature

def extract_extra_args_and_kwargs(f, *args, **kwargs):
    sig = signature(f)
    bound = sig.bind(*args, **kwargs)
    return bound.arguments['args'], bound.arguments['kwargs']

def fn(a, b, c='default', *args, **kwargs):
    print(f'a={a}, b={b}, c={c}')
    print(f'args={args}')
    print(f'kwargs={kwargs}')
args = (1, 2, 3, 4)
kwargs = {'extra_kw': 5}
print(extract_extra_args_and_kwargs(fn, *args, **kwargs))

This outputs:

((4,), {'extra_kw': 5})
like image 92
blhsing Avatar answered Sep 21 '22 13:09

blhsing


This is inspect.getcallargs:

>>> import inspect
>>> def fn(a, b, c='default', *args, **kwargs):
...   pass
...
>>> inspect.getcallargs(fn, 1,2,3,4, extra_kw=5)
{'a': 1, 'b': 2, 'c': 3, 'args': (4,), 'kwargs': {'extra_kw': 5}}

Your extract_extra_args_kwargs as defined in your question would be a simple wrapper.

def extract_extra_args_kwargs(fn, args, kwargs):
    x = inspect.getcallargs(fn, *args, **kwargs)
    return x['args'], x['kwargs']
like image 26
chepner Avatar answered Sep 22 '22 13:09

chepner