On Thu, Dec 9, 2021 at 3:15 PM Jonathan Goble <jcgoble3@gmail.com> wrote:
My preferences to resolve this are, in order:
1. Introduce `from __future__ import late_default`. When present, argument defaults in that file are late bound instead of early bound. After a suitable deprecation period, make the future statement the default behavior. Then Python will comply with best practices demonstrated by Steven's language review. I have not done any analysis, but I believe based on intuition that any breakage in libraries and scripts stemming from this would be relatively easy to fix, and most existing code should just work (in particular, the common existing usage of a sentinel as a default with an `is None` or `is sentinel` check in the body would not break and could be migrated to the new behavior at leisure). If true, it would result in minimal fuss for maximum benefit.
IMO this is strictly worse than supporting both alternatives with syntactic differences. The language still needs to support both, programmers still need to comprehend both, but instead of being able to distinguish "def f(x=[]):" from "def f(x=>[]):", you have to go look at the top of the file to see which way around it is. To the extent that the distinction needs to be visible, it needs to be visible at the function's definition, not at the top of the file.
2. If a future statement and behavior change is deemed too disruptive, then keep early binding, do not introduce late binding, and introduce a new use for the keyword `pass` to represent an absent argument. Under this idea, `pass` would be accepted as an expression in the following three contexts, and exactly nowhere else: a) as a complete argument to a call, b) as a complete "value" for an argument default, and c) in the expressions `x is pass` and `x is not pass`, but only when both `x` is a parameter to the immediately enclosing function and the default value of that parameter is `pass`. This way, `pass` acts as a sentinel that isn't a valid value in any other context, which would solve the issue of when `None` is a valid value.
This is a good idea that desperately needs good syntax. I don't like "pass" used in this way. It's perfectly implementable but only if someone can figure out how to write it. (I'd define it as "the default is for the variable to be unbound" and "if the variable is unbound". That makes very good sense and would work within the language.) Unfortunately this still has several of the problems that argument defaults are supposed to solve. It means that you can mark a parameter as optional, but you get no information about what it would be if omitted. That's just as bad as the current sentinel option, with the only advantage being that there's no sentinel. Option 2 might actually make a good extension beyond PEP 671, but it's not a replacement for it. ChrisA