13

Is it possible to pass arbitrary number of named default arguments to a Python function conditionally ?

For eg. there's a function:

def func(arg, arg2='', arg3='def')

Now logic is that I have a condition which determines if arg3 needs to be passed, I can do it like this:

if condition == True:
    func('arg', arg2='arg2', arg3='some value')
else:
    func('arg', arg2='arg2')

Question is, can I have a shorthand like:

func('arg', 'arg2', 'some value' if condition == True else # nothing so default gets picked
)

4 Answers4

10

The only way I can think of would be

func("arg", "arg2", **({"arg3": "some value"} if condition == True else {}))

or

func("arg", "arg2", *(("some value",) if condition == True else ()))

but please don't do this. Use the code you provided yourself, or something like this:

if condition:
   arg3 = "some value",
else:
   arg3 = ()
func("arg", "arg2", *arg3)
Sven Marnach
  • 574,206
  • 118
  • 941
  • 841
  • 2
    What is it you have against ** and *? – Erin Jan 12 '11 at 15:46
  • 2
    Nothing against `*` and `**`. I just think the original code is more readable. I think I don't like `a if c else b`. – Sven Marnach Jan 12 '11 at 16:04
  • 1
    Alternatively, some conditional logic could be used (outside of the function call) to construct a dictionary of arguments. They could then be passed with `**`. This stays readable with a larger number of arguments. – Wilduck Jan 12 '11 at 17:40
9

That wouldn't be valid Python syntax you have to have something after else. What is done normally is:

func('arg', 'arg2', 'some value' if condition else None)

and function definition is changed accordingly:

def func(arg, arg2='', arg3=None):
    arg3 = 'def' if arg3 is None else arg3
SilentGhost
  • 307,395
  • 66
  • 306
  • 293
5

If you have a function that has a lot of default arguments

def lots_of_defaults(arg1 = "foo", arg2 = "bar", arg3 = "baz", arg4 = "blah"):
    pass

and you want to pass different values to some of these, based on something else going on in your program, a simple way is to use ** to unpack a dictionary of argument names and values that you constructed based on your program logic.

different_than_defaults = {}
if foobar:
    different_than_defaults["arg1"] = "baaaz"
if barblah:
    different_than_defaults["arg4"] = "bleck"

lots_of_defaults(**different_than_defaults)

This has the benefit of not clogging up your code at the point of calling your function, if there is a lot of logic determining what goes into your call. You'll need to be carefull if you have any arguments that don't have defaults, to include the values you're passing for those before passing your dictionary.

Wilduck
  • 13,822
  • 10
  • 58
  • 90
0

You can write a helper function

def caller(func, *args, **kwargs):
    return func(*args, **{k:v for k,v in kwargs.items() if v != caller.DONT_PASS})
caller.DONT_PASS = object()

Use this function to call another function and use caller.DONT_PASS to specify arguments that you don't want to pass.

caller(func, 'arg', 'arg2', arg3 = 'some value' if condition else caller.DONT_PASS)

Note that this caller() only support conditionally passing keyword arguments. To support positional arguments, you may need to use module inspect to inspect the function.

Jason Lin
  • 166
  • 2
  • 4