On Sun, 11 May 2014 13:30:03 +1000, Chris Angelico wrote: > On Sun, May 11, 2014 at 1:17 PM, Steven D'Aprano > <steve+comp.lang.pyt...@pearwood.info> wrote: >> But that is an implementation detail. IronPython and Jython use an >> ordinary dict for local variable namespaces, just like globals. >> Consider this example from Jython: >> >>>>> spam = 9999 >>>>> def modify(namespace): >> ... namespace['spam'] = 42 >> ... >>>>> def demo(): >> ... modify(locals()) >> ... spam = spam >> ... return spam >> ... >>>>> demo() >> 42 > > All you're proving here is that, in some Pythons, locals() is writeable. > What happens if you remove the "spam = spam" line? Would demo() not then > return spam from the global scope,
Yes, this. See my previous email, and take careful note of Rule #2: in the absence of a binding operation, variables are not treated as local. > because the variable does not exist at local scope? No to this. Consider this example: >>> spam = 9999 >>> def modify(namespace): ... namespace['spam'] = 42 ... >>> def demo2(): ... assert 'spam' not in locals() ... modify(locals()) ... assert 'spam' in locals() ... return spam ... >>> demo2() 9999 This proves that the spam variable *does* exist in locals, but it is not seen because the "return spam" doesn't check the local scope, so it sees the global spam. Sadly, the version of Jython I have doesn't provide a working dis module, but if it did I expect it would show the equivalent of what CPython does: the first version uses LOAD_FAST to look up "spam", and the second version used LOAD_GLOBAL (a misnomer since it doesn't *just* look up globals). > Every example you've shown is simply giving a value to > something that you've created by the normal method of having an > assignment inside the function. Nonsense. Look at the original examples again, more closely. Here they are again, this time with comments: def test(): if False: spam = None # Dead code, never executed. d = locals() d['spam'] = 23 # Not a normal assignment. return spam def test(): locals()['spam'] = 42 # Not a normal assignment. return spam spam = None # Dead code. and from my reply to Devin: def modify(namespace): namespace['spam'] = 42 def demo(): modify(locals()) # Not an ordinary assignment. spam = spam # Where does the initial value of spam come from? return spam The *only* purpose of the dead code in the two test() functions is to force the compiler to use LOAD_FAST (or equivalent) rather than LOAD_GLOBAL. But they aren't ever executed. At no time do I use normal name binding assignment to create local variables. In the demo() function, if I expand the line "spam = spam" out: temp = spam # Look up of spam occurs first. spam = temp # Binding occurs second. the difficulty should be even more obvious. Where does the value of spam come from before the binding? The answer is that it comes from writing directly to the namespace (a dict). There is no fixed slot for spam waiting for a value, because Jython and IronPython don't use fixed slots. There's only a dict. If we were using globals, it would be be more obvious what was going on, and there would be no argument about whether or not a variable exists before you give it a value. The answer would be, of course it doesn't exist before it has a value. Python declarations (whether implicit or explicit) don't create variables, they just instruct the compiler how to perform lookups on them. # Global scope print spam # fails, because there is no spam variable yet "spam" in globals() # returns False globals()['spam'] = 42 # now it exists spam = spam # a no-op print spam It's only because CPython is special, and locals() is special, that the equivalent code is indeterminate inside functions. -- Steven D'Aprano http://import-that.dreamwidth.org/ -- https://mail.python.org/mailman/listinfo/python-list