I don't understand what is special about functions? Why can't you set the default value for an argument called `foo` to be a function `bar` by writing `foo=bar`?
I don't understand what is special about functions? Why can't you set the default value for an argument called `foo` to be a function `bar` by writing `foo=bar`?
I presume this got accepted because it fixes a well-known gotcha with default parameters in Python due to early evaluation, where, for instance, the dictionary instance in `def fun(args={}): …` would be shared between all invocations, leading to all sorts of fun bugs. This is especially pernicious as most Python programmers will know other languages as well, where this tends to be handled much more sensibly (e.g. in C++, D, …) and default arguments are evaluated at each call site.
For example I can define:
def f(x, l=>len(x)):
print(l)
where the default value of l depends on the other variable x.Also the “arrow” is clearly pointing the wrong way, it should be “<=“.
Why not just do `l = len(x) if l is None else l`?
With the new syntax can you define defaults computed from other defaults? What about side effects? Why is computation happening in function args at all?!
if foo is None:
foo = []
and it'd be nice to be done with that.Short answer is these downsides were discussed but the proposal went ahead anyway.
Imo I'm with you. This type of syntax has a lot of implicit behavior where it's going to be difficult to find/fix bugs related to it.
Because dev tooling can “see” and present information from the argument list much more easily than analyzing downstream behavior.
> What about side effects?
Those happen whether or not the computation is visible in the signature (though its more obvious to the programmer of the consuming code if it is visible.)
However this comes up a lot for new engineers during programming interviews. It's very common to see recursive algos use empty lists as default/start-case args:
def traverse(graph, seen_so_far=[]): # Incorrect
....
seen_so_far.append(node)
Subsequent calls to `traverse` will have state accumulated from earlier calls. The right solution is: def traverse(graph, seen_so_far=None):
if not seen_so_far:
seen_so_far = []
.....
This isn't obvious if you're new to Python. This PEP makes this (arguably) clearer with: def traverse(graph, seen_so_far=>[]):It's more concise when you are familiar with it, and it improves introspection and dev tooling when functions use it, but it is not fundamentally clearer.