Skip to content

Comment on The Perils of Default Arguments in Python

Comments

Yes, this is the subject of one of the "classic" posts of OG Stack Overflow (https://stackoverflow.com/questions/1132941). And in fact it commonly gets deliberately abused (https://stackoverflow.com/questions/3431676) despite better tools being available for the intended effect (https://stackoverflow.com/questions/277922).

But it's good to have more writeups, I suppose. And it's good to point out that the initializers of a `dataclass` will show the same kind of early binding, and how to use `default_factory` instead. (It's not immediately obvious that this is related to the issue with function arguments, but it is.)

That is a very roundabout way of saying that if you do this... the default argument value for base_url will be "eaj.no", and not "example.com".

This isn't a good example. Even in a hypothetical alternate Python where `base_url` had its default value read at call time, `some_function` isn't called anywhere, nor does it modify the global `default_url`, so there's no reason why the behaviour of `get_post_url` would change.

Notably, we can also pass a variable as the default argument value.

No; as already established, a value is used - the value that results from evaluating the expression `default_url` (which happens to consist of just a variable name). Also, default argument values aren't "passed" - that's the point.

In short, default arguments should never be used for values that are required to change between function calls.

More accurately, subsuming the previous section: they should not be used for values that must be determined at the time the function is called. Because that isn't what happens.

Use None as a sentinel value and conditionally create the mutable default value in the function body instead.

This is the standard advice, but I hate it. "Special cases aren't special enough to break the rules"; `None` is overly magical in a lot of Python code. Besides, writing code that modifies the passed-in value and then also `return`s a value isn't Pythonic - https://en.wikipedia.org/wiki/Command%E2%80%93query_separati... is a key part of the design of the standard library and of several builtin methods. Maybe you expect the caller to use a temporary for every call, but maybe the caller didn't understand that.

A lot of the time, a better approach is to just use an equivalent immutable value.

AboutSource Built by g1lg1l

Hackerly is an independent reader for Hacker News, built on the public HN API. Not affiliated with Y Combinator.