6 ms·
Biggest footgun in python IMO are mutable default parameters. It amazes me that someone thought this was a good idea.
by lysecret 3y ago
Biggest footgun in python IMO are mutable default parameters. It amazes me that someone thought this was a good idea.
- lysecret 3y agoAlso a most random note here: mutable /ˈmjuːtəbl/ adjective liable to change. I like the emphasis that it’s liability haha
- Spivak 3y agoThat's not really what's going on. Python has default values where, say Ruby, has default expressions. def myfunc(x=[]) puts x end In Ruby is the same as def myfunc(x=lambda:[]) print(x()) in Python when the default is needed. The benefit is performance, you only have to evaluate the default once at function definition time. I won't say it's not a footgun but it's also I think a sane choice when in Python it's common to have functions with many default parameters.
- o1y32 3y ago"the benefit is its performance" Sure, but when you design a language you are striking a balance between many things (which is why some features could take years to finalize and implement). In this case, the gain in performance is likely minimal, but the confusion and surprise it causes does a lot of damage. (I recently read through the entire series of conversation with Anders Hejlsberg on the design of C# which is very insightful https://www.artima.com/intv/anders.html https://www.artima.com/intv/anders.html)
- crabbone 3y agoYou are too naive when you think that someone thought about it. Many things in Python happened because that's how they were implemented initially without much thinking or at all. There was no plan, and, in the initial stages, I'd imagine that the author(s) were surprised that they'd even gotten this far. Famously, the whole object system in Python was implemented in like a few days. That included the design and testing too... It did see some changes over time, but there's a lot of petrified turd there that's increasingly difficult to get rid of.
- mixmastamyk 3y agoArrogant bullshit, probably the worst non-spam comment I’ve seen on HN—congrats. https://peps.python.org/pep-0671/ https://peps.python.org/pep-0671/ Truth is, early vs late binding is a tradeoff. Python picked one, but not like the other is without edge cases either.
- colejohnson66 3y agoGP's point is that Python didn’t “pick” one but that it’s probably an accident of how the parser was implemented. It would see the `[]`, allocate a new object, then greedily assign it to the “variable”. The whole “tradeoff” argument doesn’t make sense when you consider that Python is the odd one out here with early binding. In fact, there’s been so many bugs with it that they had to invent a whole new syntax to fix it. When one says `intParam += 2`, why should they expect that to modify the default parameter? Yet when the default is a list, all of a sudden it’s fine. Python breaks the principle of least astonishment with default parameters, and it’s frustrating hearing people defend such a thing.
- mixmastamyk 3y agoThe point is that it wasn't an accident. It was picked for performance reasons. And Python isn't the only one that does it. Don't remember the exact list but it was given once in a python-dev thread regarding the mentioned pep.
- chlorion 3y agoI don't understand what's getting you all bent out of shape about this. Unexpected emergent behaviors are extremely common in programming language design. Even when carefully planning every language feature, unexpected interactions can and do occur, sometimes these things are not ideal. I am not very informed about Python's history here, but it's not far fetched that this behavior was not planned and just happened due to unexpected interactions of other features. Why would Python be immune to this? Are Guido and the rest of the Python contributors 1000x devs that can see infinitely far into the future and never make mistakes? All of Pythons quirks were actually planned from the very beginning I guess.
- sfink 3y agoFor lists, use a tuple as default, since it's immutable. def foo(allowed=()): truthy = ['true', 'True'] truthy += allowed # most things work allowed.append('yes') # error It's not perfect (`truthy = ['true', 'True'] + allowed` will not work).
- MawKKe 3y agowhile that works, it does not fully document what is expected from caller. And AFAIK using tuples like that is not very common. I'd prefer something like: def foo(allowed: Optional[List[ElemType]] = None): allowed = allowed or [] ...
- boothby 3y agoFor what it's worth, mutable default parameters do have their use. def foo(x, cache = {}): value = cache.get(x) if value is None: ... cache[x] = value return value This simple example, thankfully, is obviated by the @cache decorator as of py3.9, but I still occasionally find uses for the feature.