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