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 -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at
https://mail.python.org/archives/list/[email protected]/message/7WNZUZK2X7EH7VCCHVPFTM7ZX6GCUIY7/
Code of Conduct: http://python.org/psf/codeofconduct/