Reputation: 125
How to pass named arguments to a function from another function?
I have looked at wrappers, argparse, Google for the past 8 hours. I am missing something big time.
def my_func(a='something', b='something_else'):
# add a and b
return something
def slave(func, args):
result = func(args)
return result
slave_work = {my_func, (a=50, b=90)}
print (slave(slave_work)
Should output 140
. How can I achieve that?
Upvotes: 4
Views: 4014
Reputation: 50116
Python allows to un/pack iterables to positional parameters and mappings to keyword parameters. The syntax is *arguments
and **keywords
, respectively.
>>> def my_func(a, b):
... return a - b
...
>>> args = [2, 1] # iterable for positional arguments
>>> my_func(*args) # unpack iterable
1
>>> kwargs = {'b': 1, 'a': 2} # mapping for keyword arguments
>>> my_func(**kwargs) # unpack mapping
1
You can even combine both forms of variadic arguments - e.g. my_func(*args, **kwargs)
.
To create a wrapper function that calls another, you can use this to either:
Explicitly pass in iterables/mappings:
# take regular arguments...
def slave(func, args, kwargs):
# ...and unpack them
result = func(*args, **kwargs)
return result
# explicit iterable [1] and mapping {'b': 2}
print(slave(my_func, [1], {'b': 2}))
# from existing args/kwargs
args, kwargs = [1], {'b': 2}
print(slave(my_func, args, kwargs))
Explicit iterables/mappings are simpler for scripted interfaces, i.e. when you have args
/kwargs
for use in slave(func, args, kwargs)
. They are rather cumbersome for manual use, i.e. when you call slave(func, [1], {'b': 2})
.
Convert passed in parameters:
# take variadic arguments...
def slave(func, *args, **kwargs):
# ...and pass them on
result = func(*args, **kwargs)
return result
# explicit iterable [1] and mapping {'b': 2}
print(slave(my_func, 1, b=2))
# from existing args/kwargs
args, kwargs = [1], {'b': 2}
print(slave(my_func, *args, **kwargs))
Implicit iterables/mappings are simpler for manual use, i.e. when you call slave(func, 1, b=2)
. They are costly for scripted interfaces, i.e. when you have args
/kwargs
and must relatedly unpack them in slave(func, *args, **kwargs)
.
Upvotes: 6
Reputation: 19414
What you are doing now is simply passing a set to slave
as a single argument. So you're probably getting a TypeError
on a positional argument missing.
You might want to change your slave_work
to be a dict (right now it is a set), and then it would look like:
slave_work = {'func': my_func, 'args': {'a': 50, 'b': 90}}
And now you can unpack the dict by doing:
print(slave(**slave_work))
This is more or less equivalent for doing:
print(slave(func=slave_work['func'], args=slave_work['args'])
Then inside your slave
function change accordingly to:
result = func(**args)
Another option is to use list (or tuple in this case) unpacking. So your slave_work
can also be:
slave_work = {'func': my_func, 'args': (50, 90)}
And then your call to slave
will be the same, but inside slave
change to:
result = func(*args)
The difference is that this will unpack the arguments according to their position and not their name.
Upvotes: 7