DaftVader
DaftVader

Reputation: 125

Passing arguments to a function with named arguments

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

Answers (2)

MisterMiyagi
MisterMiyagi

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

Tomerikoo
Tomerikoo

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

Related Questions