0

Assume I'm calling a function fu() inside another function f2(). I would like to pass to fu() some of the optional arguments. Is it possible?

def f0( arg1, arg2 = None):
    print( arg1 )
    if arg2 is not None:
        print( f'arg2 of f0() is: {arg2}' )

def f1( arg1, arg2 = None, arg3 = None ):
    print( arg1 )
    if arg2 is not None:
        print( f'arg2 of f1() is: {arg2}' )
    if arg3 is not None:
        print( f'arg3 of f1() is: {arg3}' )

def f2( fu, arg1, *args, **kwargs ):
    fu( arg1, ??? )

I'm wondering if it is possible to write the code above in a way that something like this would work:

f2(f0, 1)
> 1

f2(f0, 1, 2)
> 1
> arg2 of f0() is: 2

f2(f0, 1, 2, 3)
> Error

f2(f1, 1)
> 1

f2(f1, 1, 2)
> 1
> arg2 of f1() is: 2

f2(f1, 1, 2, 3)
> 1
> arg2 of f1() is: 2
> arg3 of f1() is: 3

???
> 1
> arg3 of f1() is: 2

If a solution does not exist, maybe something similar could be achieved by using dictionaries?

UPDATE

The question above was answered by @keanu in the comment to this question. Here is an extended version.

Now I would like to pass two functions to f2(), as well as optional parameters to those. My idea was to somehow use dictionaries:

def f2( fu0, fu1, arg10, arg11, f0_args = None, f1_args = None ):
    fu0( arg10, f0_args )
    fu1( arg11, f1_args )

This, however, does not work:

f2( f0, f1, 1, 2, f1_args = { 'arg3' : 4 } )
> 1
> 2
> arg2 of f1() is: {'arg3': 4}

While I would like to get

> 1
> 2
> arg3 of f1() is: 4

Is there a way to do this?

YAAAAAY, SEEMS TO BE WORKING:

def f2( fu0, fu1, arg10, arg11, f0_args = None, f1_args = None ):
    if f0_args is None:
        fu0( arg10 )
    else:
        fu0( arg10, **f0_args )
    if f1_args is None:
        fu1( arg11 )
    else:
        fu1( arg11, **f1_args )

Not too elegant though...

mavzolej
  • 200
  • 2
  • 8
  • 2
    fu( arg1, *args, **kwargs) – TheSinisterShadow Feb 12 '21 at 05:16
  • Damn, python is SO smart... – mavzolej Feb 12 '21 at 05:20
  • @keanu, Can I define `f2()` as `def f2( fu, arg1, **kwargs ): fu( arg1, **kwargs )`? Apparently `*args` is never used... – mavzolej Feb 12 '21 at 05:25
  • Yes you can. But what do you mean *args is never used? If you pass in additional positional arguments, like f2(fu, 3, 5) then args should be a tuple of value (5,). If you only use kwargs then you have to specify the keyword. – TheSinisterShadow Feb 12 '21 at 05:27
  • I see. So I may want to remove `*args` intentionally, if I want to enforce the user to explicitly specify the names of optional arguments for `fu()`? – mavzolej Feb 12 '21 at 05:31
  • Yes. If you only use `**kwargs` then a call like `f2(fu, arg1, 2, 3, 5)` will return an error. Instead you have to do `f2(fu, arg1, arg2=arg2, arg3=arg3)`... – TheSinisterShadow Feb 12 '21 at 05:32
  • Awesome!! Thanks a lot. – mavzolej Feb 12 '21 at 05:33
  • @keanu, please see an update to my question. – mavzolej Feb 12 '21 at 05:57
  • @keanu, I think I got it!! See another update. – mavzolej Feb 12 '21 at 06:06
  • 1
    Sorry, I was sleeping. There is a better way to do this. Since `*` and `**` are actually just taking tuples and dictionaries and converting them into arguments for functions, just set the default arguments for `f0_args` and `f1_args` to `{}` instead of `None`. Now you don't need the if else statements. – TheSinisterShadow Feb 12 '21 at 14:59
  • Is it OK, that Pycharm gives a warning **Default argument value is mutable**? – mavzolej Feb 12 '21 at 16:46
  • This should be fine as long as you do not modify the arguments in the function. Mutable default arguments will carry over changes, so for example if you had a function `def f(a=[])` and inside the function you modified the list, `a.append(6)` then the next time you call f with default arguments `a` will be `[6]`, not `[]`. Read more https://stackoverflow.com/questions/1132941/least-astonishment-and-the-mutable-default-argument – TheSinisterShadow Feb 12 '21 at 16:50
  • I guess, another valid solution would be to keep the default value as `None` and use `fu0(arg1, **arg2 if arg2 else {})`. – mavzolej Feb 12 '21 at 16:54
  • 1
    Yes. That is the recommended practice. I believe you can shorten this to `fu0(arg1, **(arg2 or {}))` – TheSinisterShadow Feb 12 '21 at 16:55
  • [Nice!](https://codegolf.stackexchange.com/) – mavzolej Feb 12 '21 at 16:58

1 Answers1

0

Rather than passing function as a argument, call fu function inside f2 and use **kwargs to pass arguments inside fu. See below example.

def fu(a=5, b=7, c=3):
    return a*b*c

def f2(**kwargs):
    x = fu(**kwargs)
    return x

y = f2(a=4, b=3)
print(y)

Note: only pass the function as argument if it's going to change

Sociopath
  • 13,068
  • 19
  • 47
  • 75