First, let me state that I am in favour of the proposal (although still mildle prefer the ":=" spelling). On 17/06/2022 13:33, Chris Angelico wrote:
On Fri, 17 Jun 2022 at 22:14, Steven D'Aprano <steve@pearwood.info> wrote:
If we have: ``` items = ['spam', 'eggs'] def frob(n=>len(items), items=[]): print(n) ``` we cannot even tell whether `frob()` will print 0 or 2 or raise an exception.
It will either print 0 or raise UnboundLocalError. There is no circumstance in which it will legally print 2.
Under the PEP though, this behaviour is underspecified. The PEP describes this case as implementation dependent. Any of the following behaviours would be legal when `frob()` is called:
* n=>len(items) evaluates the parameter `items`, *after* it gets bound to the default of [], and so n=0 (that is, it has the same semantics as the status quo);
Yes, this is legal.
* n=>len(items) evaluates the parameter `items`, but it isn't bound to a value yet (because `items` occurs to the right of n), and so evaluating the default raises (presumably) UnboundLocalError;
Yes, this is legal.
* n=>len(items) evaluates the variable items from the surrounding scope, and so evaluates to n=2; if no such variable exists, it will presumably raise NameError.
No, this makes no sense whatsoever. In Python, a parameter is (effectively) assigned to within the function, and therefore *any* reference to it *must* refer to the local, not to any surrounding scope. Late-bound defaults do not change this fundamental.
I understand this is unambiguous, but it is nonetheless potentially confusing: normal, immediate-evaluation arguments do, of course, have access to enclosing scope, and so one might be led to believe that this is still possible.
With the behaviour unspecified, we can't predict whether the above frob() example is legal or what it will do if it is. It could vary not only between CPython and other Pythons, but from one version of CPython and another.
That is correct. This issue ONLY happens if a late-bound default refers to an early-bound argument that comes to the right of it in the argument list, and the ONLY possible results are UnboundLocalError and getting the value.
Is there a *reason* why you are leaving this unspecified? To put it more baldly, is there any reason (e.g., difficulty of parsing?) why allowing these "forward" references should *not* be allowed? It seems that "n=>len(items), items=[]" might be an important use case. Andrew