This is the most Pythonic way:
def foo(arg=None):
if arg is None:
arg = bar()
...
If you want the function bar to be called only once, and you don't want it to be called at import time, then you will have to maintain that state somewhere. Perhaps in a callable class:
class Foo:
def __init__(self):
self.default_arg = None
def __call__(self, arg=None):
if arg is None:
if self.default_arg is None:
self.default_arg = bar()
arg = self.default_arg
...
foo = Foo()
Answer from wim on Stack Overflowpython - Function call as a default function argument - Stack Overflow
python - Calling a function with default value for arguments - Stack Overflow
How to use default values and arbitrary arguments at one function call in Python? - Stack Overflow
optionally using default parameters when calling a function
Just let them be None and set them at execution time.
def A(argA1=None, argA2=None, argA3=None):
argB1val= (K * argA1) if argA1 is not None else defValA1
argB2val= (K * argA2) if argA2 is not None else defValA2
argB3val= (K * argA3) if argA3 is not None else defValA3
B(argB1=argB1val, argB2=argB2val, argB3=argB3val)
def B(argB1=defValB1, argB2=defValB2, argB3=defValB3):
print("First argument: " + argB1)
print("Second argument: " + argB2)
print("Third argument: " + argB3)
You can populate a dict with all the named arguments you want for you B method and use an unpacking operator (**) to call the B method. All the arguments not in the dictionary your create will get the default values of the B method.
def A(argA1=defValA1, argA2=defValA2, argA3=defValA3):
kwargs = {}
if argA1 != defValA1:
kwargs["argB1"] = argA1
if argA2 != defValA2:
kwargs["argB2"] = argA2
if argA3 != defValA3:
kwargs["argB3"] = argA3
B(**kwargs)
If you use None for the default values it becomes:
def A(argA1=None, argA2=None, argA3=None):
kwargs = {}
if argA1 is not None:
kwargs["argB1"] = argA1
if argA2 is not None:
kwargs["argB2"] = argA2
if argA3 is not None:
kwargs["argB3"] = argA3
B(**kwargs)
Mixing parameters with and without default values can indeed be confusing. Parameters are the names used in the function definition, arguments are the values passed into a call.
When calling, Python will always fill all parameters from positional arguments, including names with default values. size is just another parameter here, even though it has a default value. You can also use name=value syntax in a call to assign an argument value to a specific parameter (whether or not they have a default value). But you can't tell Python not to assign something to size, not with your current function definition, because everything before the *toppings parameter is always going to be a regular positional parameter.
The *toppings parameter will only capture any positional arguments after all the other parameters have received values. So 'onion' is assigned to size, and the remainder is assigned to *toppings.
In Python 3, you can make size a keyword-only parameter, by placing them as name=default after the *toppings name, or an empty *:
def make_pizza(*toppings, size=15):
Now size can only be set from a call with size=<new value> keyword argument syntax.
In Python 2, you can only capture such keyword arguments with a **kwargs catch-all parameter, after which you need to look into that dictionary for your size:
def make_pizza(*toppings, **kwargs):
size = kwargs.get('size', 15) # set a default if missing
In both cases, you have to remember to explicitly name size, and put such explicitly named keyword arguments after the positional arguments:
make_pizza('ham', 'extra meat', 'sweet con', 'pepperoni', size=17)
There's a fundamental problem with your approach: It's ambiguous because how do you know if you intended it as size or as topping? Python can't do that so you need to find an alternative.
Well, you could simply remove size from the argument list and interpret the first *toppings argument as size if it's an integer:
def make_pizza(*toppings):
if toppings and isinstance(toppings[0], int):
size, toppings = toppings[0], toppings[1:]
else:
size = 15
print("\nMaking a {}-inch pizza with the following toppings: ".format(size))
for topping in toppings:
print("- " + topping)
However that will fail for cases where a simple type check isn't possible. Maybe the better approach would be to make toppings a normal argument and size an optional argument:
def make_pizza(toppings, size=15):
print("\nMaking a {}-inch pizza with the following toppings: ".format(size))
for topping in toppings:
print("- " + topping)
However then you need to pass in a sequence for toppings and it changes the order of toppings and size but it's probably a lot cleaner.
make_pizza(['onion','shrimp','goda cheese','mushroom'])
make_pizza(['ham','extra meat','sweet con','pepperoni'], 17)
You could also keep the toppings as arbitary positional arguments and make size a keyword-only parameter with default (Python3-only):
def make_pizza(*toppings, size=15):
print("\nMaking a {}-inch pizza with the following toppings: ".format(size))
for topping in toppings:
print("- " + topping)
make_pizza('onion','shrimp','goda cheese','mushroom')
make_pizza('ham','extra meat','sweet con','pepperoni', size=17)
Hi is there a way of saying if variable x as a certain value then call a function using an arguments default value else call it using a set value.
So is there a better way of doing
if x == None: y = f() else: y=f(myarg=x)
I've tried googling it but just get pages upon pages of how to define function to use default values.
EDIT: While I appreciates the responses given so far, it seems I've simplified the example too much.
The actual function is a call to a third party module, which as a number of optional arguments, many of which expect objects returned from other function calls, some of which may capture state when defined .
Some of the optional arguments do not accept "None" as a legitimate value.
I wasn't looking for where to put the if: statement I was hoping there was a bit of syntactical sugar to say use the default value Which I guess isn't the case.