On Sun, Oct 2, 2016 at 9:20 PM, Steve D'Aprano <steve+pyt...@pearwood.info> wrote: > On Sun, 2 Oct 2016 04:06 pm, Chris Angelico wrote: >> 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.
The only way to prove that something is a new binding is to demonstrate that, when this binding is removed, a previous one becomes visible. In Python, that only ever happens across namespaces, and in CPython, the only way I can make it happen is with globals->builtins. > steve@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. Beautiful, thank you. > 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. Interesting. The real question, then, is: Are function-local names inherently special? I can't make anything else do this. Class namespaces don't shadow globals or builtins, even during construction: >>> class X: ... str = "test" ... def func(self): ... print(self) ... return str ... print("123 =", func(123)) ... print("str =", func(str)) ... 123 123 = <class 'str'> test str = <class 'str'> >>> X().func() <__main__.X object at 0x7f98643cc2b0> <class 'str'> The 'str' as a function parameter uses the class's namespace, but in the function, no sir. (As it should be.) I don't know of any other namespaces to test. > 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. Yep. Would the same have happened if you'd omitted the "if False" part, though - that is, if 'x' were not a local name due to assignment, could it *become* local through mutation of locals()? I would just grab IronPython and test, except that it isn't available in the current Debian repositories: https://archive.debian.net/search?searchon=names&keywords=ironpython This most likely means I'll have to do some fiddling around with deps to get it to install :( ChrisA -- https://mail.python.org/mailman/listinfo/python-list