hirose31
hirose31

Reputation: 43

How to return *args, **kwargs by function

Simple function which takes argument list and keyword argument:

def foo(*args, **kwargs):
    print(args, kwargs)

I can call this function as follows

foo('foo', 'bar', 'baz', hoge='H', fuga='F')

or

l = ['foo', 'bar']
kw = {'hoge': 'H', 'fuga': 'F'}
foo(*l, **kw)

QUESTION: Can I pass arguments by another function?

foo(produce_arg())

I'm writing CLI script with Click https://palletsprojects.com/p/click/. Several subcommands takes same options:

@click.group()
def cli():
    pass

@cli.command()
@cli.option('--target', type=str, ...)
@cli.option('--exec', is_flag=True, ...)
...
def foo():
    pass

@cli.command()
@cli.option('--target', type=str, ...)
...
def bar():
    pass

@cli.command()
@cli.option('--exec', is_flag=True, ...)
...
def baz():
    pass

...

I think it's not DRY, so I want to write as follows:

def definition_of(optname):
    # so magical code!

@click.group()
def cli():
    pass

@cli.command()
@cli.option(definition_of('target'))
@cli.option(definition_of('exec'))
...
def foo():
    pass

@cli.command()
@cli.option(definition_of('target'))
...
def bar():
    pass

@cli.command()
@cli.option(definition_of('exec'))
...
def baz():
    pass

...

Any ideas?

Upvotes: 4

Views: 1663

Answers (1)

glglgl
glglgl

Reputation: 91017

To directly address your question: Yes, you can pass arguments by another function:

a, k = produce_arg()
foo(*a, **k)

This procedure can be "hidden away", in a way:

def pass_a_k(func, ak):
    return func(*ak[0], **ak[1])
pass_a_k(foo, produce_arg())

would be one approach,

def pass_a_k(func, a, k):
    return func(*a, **k)
pass_a_k(foo, *produce_arg())

would be a slightly different approach.

Both use a helper function to call the target function.

def pass_a_k(func, a, k):
    return func(*a, **k)
def adapt_a_k(func):
    # here, maybe fool around with functools.wraps etc.
    return lambda a, k: func(*a, **k)
adapt_a_k(foo)(*produce_arg())

(or its respective counterpart) would be another approach. Here, it would be helpful to keep the "adapted" function if you need it more often.

In your example, that could be

cli_option_a_k = adapt_a_k(cli.option)

@cli_option_a_k(*definition_of('target'))
@cli_option_a_k(*definition_of('exec'))
...
def foo():
    pass

or even

cli_option_by_optname = lambda optname: adapt_a_k(cli.option)(*definition_of(optname))
cli_option_by_optname = lambda optname: pass_a_k(cli.option, *definition_of(optname))

@cli_option_by_optname('target')
@cli_option_by_optname('exec')
...
def foo():
    pass

Upvotes: 2

Related Questions