unintuitive for-loop behavior
Steve D'Aprano
steve+python at pearwood.info
Sun Oct 2 06:20:00 EDT 2016
On Sun, 2 Oct 2016 04:06 pm, Chris Angelico wrote:
> On Sun, Oct 2, 2016 at 3:19 PM, Steve D'Aprano
> <steve+python at pearwood.info> wrote:
>> In IronPython, you could have the following occur in a function locals,
>> just as it could happen CPython for globals:
>>
>> - delete the name binding "x"
>> - which triggers a dictionary resize
>> - bind a value to x again
>> - because the dictionary is resized, the new "slot" for x is in a
>> completely different position of the dictionary to the old one
>>
>> There is no user-visible difference between these two situations. Your
>> code works the same way whether it is executed inside a function or at
>> the global top level, whether functions use the CPython local variable
>> optimization or not.
>
> Hmm, interesting. I don't have IronPython here, but maybe you can tell
> me what this does:
>
> print(str)
> str = "demo"
> print(str)
> del str
> print(str)
>
> and the same inside a function. In CPython, the presence of 'str =
> "demo"' makes str function-local, ergo UnboundLocalError on the first
> reference; but globals quietly shadow built-ins, so this will print
> the class, demo, and the class again. If IronPython locals behave the
> way CPython globals behave, that would most definitely be a
> user-visible change to shadowing semantics.
That's a nice catch!
But its not a difference between "update binding" versus "new binding" --
its a difference between LOAD_FAST and LOAD_whatever is used for things
besides locals.
steve at orac:~$ ipy
IronPython 2.6 Beta 2 DEBUG (2.6.0.20) on .NET 2.0.50727.1433
Type "help", "copyright", "credits" or "license" for more information.
>>> def func():
... print(str)
... str = 1
...
>>> func()
Traceback (most recent call last):
UnboundLocalError: Local variable 'str' referenced before assignment.
Although IronPython does the same thing as CPython here, I'm not 100% sure
that this behaviour would be considered language specification or a mere
guideline. If the author of an alternate implementation wanted to specify a
single name resolution procedure:
- look for local variable;
- look for nonlocal;
- look for global;
- look for builtin;
- fail
rather than two:
(1)
- look for local;
- fail;
(2)
- look for nonlocal;
- look for global;
- look for builtin;
- fail
I'm not entirely sure that Guido would say No. I think that "fail early if
the local doesn't exist" as CPython does would be permitted rather than
mandatory. But I could be wrong.
By the way, here's an example showing that IronPython does allowing writing
to locals to affect the local namespace:
>>> def func():
... locals()['x'] = 1
... print(x)
... if False:
... x = 9999
...
>>> func()
1
And *that* behaviour is most definitely allowed -- the fact that writing to
locals() isn't supported by CPython is most definitely an implementation-
specific limitation, not a feature.
--
Steve
“Cheer up,” they said, “things could be worse.” So I cheered up, and sure
enough, things got worse.
More information about the Python-list
mailing list