[Python-ideas] A "local" pseudo-function
Tim Peters
tim.peters at gmail.com
Sun Apr 29 02:57:21 EDT 2018
[Tim Delaney <timothy.c.delaney at gmail.com>]
>>> My big concern here involves the:
>>>
>>> if local(m = re.match(regexp, line)):
>>> print(m.group(0))
>>>
>>> example. The entire block needs to be implicitly local for that to work
>>> -
>>> what happens if I assign a new name in that block?
[Tim Peters]
>> I really don't know what you're asking there. Can you make it
>> concrete? If, e.g., you're asking what happens if this appeared after
>> the `print`:
>>
>> x = 3.14
>>
>> then the answer is "the same as what would happen if `local` had not
>> been used". We can't know what that is without context, though.
>> Maybe x is global. Maybe x was declared nonlocal earlier. Maybe it's
>> function-local. ...
[Tim D]
> That's exactly what I was asking, and as I understand what you're saying, we
> would have a local name m available in the indented block which went away
> when the block ended, but any names modified in the block are not local to
> the block. That seems likely to be a source of errors.
If you what you _want_ is a genuinely new scope, yes. But no actual
use cases so far wanted that at all.
This is the kind of code about which there have been background
complaints "forever":
m1 = regexp1.match(line)
m2 = regexp2.match(iine)
if m1 and m2:
do all sorts of stuff with m1 and/or m2,
including perhaps modifying local variables
and/or global variables
and/or nonlocal variables
The complaints are of two distinct kinds:
1. "I want to compute m1 and m2 _in_ the `if` test".
2. "I don't want these temp names (m1 and m2) accidentally
conflicting with local names already in scope - if these names
already exist, I want the temp names to shadow their
current bindings until the `if` structure is done".
So,
if local(m1=regexp1.match(line),
m2 = regexp2.match(iine),
m1 and m2):
intends to address both complaints via means embarrassingly obvious to
the most casual observer ;-)
This is, e.g., the same kind of name-specific "shadowing" magically
done by list and dict comprehensions now, and by generator
expressions. For example,
[i**2 for i in range(10)]
has no effect on whatever `i` meant before the listcomp was executed.
> To clarify my understanding, if the names 'x' and 'm' did not exist prior to
> the following code, what would x and m refer to after the block completed?
>
> if local(m = re.match(regexp, line)):
> x = 1
> m = 2
I hope the explanation above made that clear. What's wanted is
exactly what the current
m = re.match(regexp, line):
if m:
x =1
m = 2
_would_ do if only there were a sane way to spell "save m's current
status before that all started and restore it after that all ends".
So they want `x == 1` after it's over, and `m` to raise NameError.
>>> if local { m = re.match(regexp, line) }:
>>> print(m.group(0))
>> OK, this is the only case in which you used it in an `if` or `while`
>> expression. All the questions you asked of me at the start can be
>> asked of this spelling too.
>> You seemed to imply at the start that the
>> right curly brace would always mark the end of the new scope. But if
>> that's so, the `m` in `m.group()` has nothing to do with the `m`
>> assigned to in the `local` block - _that_ scope ended before `print`
>> was reached.
> Yes - I think this is exactly the same issue as with your proposed syntax.
Wholly agreed :-)
>> So if you're not just trying to increase the level of complexity of
>> what can appear in a local block, a fundamental problem still needs
>> solving ;-) I suppose you could solve it like so:
>>
>> local { m = re.match(regexp, line)
>> if m:
>> print(m.group(0))
>> }
>>
>> but, besides losing the "shortcut", it would also mean something
>> radically different if
>>
>> x = 3.14
>>
>> appeared after the "print". Right? If a "local block" is taken
>> seriously, then _all_ names bound inside it vanish when the block
>> ends.
> Indeed, and I don't have a proposal - just concerns it wold be very
> difficult to explain and understand exactly what would happen in the case of
> something like:
>
> if local(m = re.match(regexp, line)):
> x = 1
> m = 2
Only names appearing as targets _in_ the `local(...)` are affected in
any way. The states of those names are captured, then those names are
bound to the values of the associated expressions in the `local(...)`,
and when the scope of the `local` construct ends (which _is_ hard to
explain!) those names' original states are restored.
So the effects on names are actually pretty easy to explain: all and
only the names appearing inside the `local(...)` are affected.
> Regarding the syntax, I didn't want to really change your proposal, but just
> thought the functionality was different enough from the function call it
> appears to be that it probably merits different syntax.
Probably so!
More information about the Python-ideas
mailing list