On Mon, May 25, 2020 at 02:03:49AM +0100, Rob Cliffe via Python-ideas wrote:
[quoting Dominik Vilsmeier] > >It looks like the most common use case for this is to deal with > >mutable defaults It might be the most common use case, but it's hardly the only use case. The PEP talks about special cases :-) I can tell you that it is very frustrating to what to do something and not be able to do it, not for good design reasons, or for implementation reasons, but simply because the language designers chose to support only a subset of behaviour. Mutable defaults is a subset of late-binding defaults. A solution to late-binding automatically solves the mutable default question. Why settle for half a solution? Looking at some of my mutable defaults, I've used the usual `[]` and `{}` of course, but I've also used `[0]` amd other pre-populated lists, sets and dicts, and instances of custom classes. I've also used default values that are functions themselves, which are technically mutable even if we don't typically mutate them. I've also used plenty of *immutable* defaults, where I have wanted them recalculated on each use. [Rob] > Idea: Invent a new kind of string which behave like f-strings, called > say g-strings. G-strings? *cough* > Add a rule that if the default value of an argument is > (an expression containing) a g-string, > the default value is recalculated every time the function > is called and a value for that argument is not passed. That's pretty much the same rule I've been working on for my proto-PEP, except for the "g-string" part. I know that f-strings have become super-popular, but that doesn't make every piece of syntax a nail that we hammer with the "random-letter"- string until we have 26 different string prefixes :-) [...] > (Possibly heretical) Thought: > ISTM that when the decision was made that arg default values should be > evaluated > once, at function definition time, > rather than > every time the function is called and the default needs to be > supplied that that was the *wrong* decision. It really wasn't a mistake. Look at your default values: the great majority of defaults need to be evaluated only once. Making every default value a late-bound expression would be wasteful of time and memory. (Although a sufficiently smart compiler could minimize that waste in the common case of immutable literals. How? By sneakily shifting to early binding and avoiding the late evaluation!) There's also at least two execution models for late binding that I know of, and which ever one you chose, some people would complain that it's not the right one. If you can only have one model for function defaults, early binding is the clear winner. With early binding, it is easy to implement late binding in the function body using the "if None: arg = value" idiom. But with late binding, it is *seriously* inconvenient and difficult to go the other way and implement early binding semantics. > But it is a constant surprise to newbies (and sometimes not-so-newbies). Yes, people say that they want late binding. Then they use closures, which implement late binding, and they complain that it's a bug and it was the wrong decision to use late binding. Then they go back to writing a function definition, and complain that they should have used late binding. I remind you that the usual work-around for the "closures use late binding" gotcha is to use *early binding* to fix it. The gotcha: py> def func(): ... L = [] ... for i in range(3): ... # closure using late binding for i ... L.append(lambda: 100+i) ... return [f() for f in L] ... py> func() [102, 102, 102] The solution is to work around the problem with early binding: py> def func(): ... L = [] ... for i in range(3): ... # work around the problem ... L.append(lambda i=i: 100+i) ... return [f() for f in L] ... py> func() [100, 101, 102] If functions used late binding for all defaults: - people would still be surprised, just as they are with closures; - it would be wasteful of time and memory, slowing down function calls for no good reason; - it would make early binding semantics really difficult; - and it would destroy the work-around for late binding, preventing it from working. Late binding of function defaults an option? Sure. But if Python had used late binding for all defaults, with no way to opt out, the language would be significantly worse: slower, heavier memory usage, and more annoying to use. -- Steven _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-le...@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/7WNZUZK2X7EH7VCCHVPFTM7ZX6GCUIY7/ Code of Conduct: http://python.org/psf/codeofconduct/