2

Possible Duplicate:
Python nested functions variable scoping

After much trial and error I have eventually discovered that this doesn't work:

def a():
    def b():
        print x
        x=2
    x = 1
    b()
    print x

You get an exception (x not defined before being referenced). So it looks like b can read from x, but if it tries to assign to it, Python changes its interpretation of 'x' to be a local variable, which is now not defined.

Question for my own sick curiosity: is there any way of achieving this? Is there a way of explicitly accessing the scope of the parent function? (x is not global)

Community
  • 1
  • 1
Steve Bennett
  • 114,604
  • 39
  • 168
  • 219
  • Duplicate of, among others, http://stackoverflow.com/q/5218895/440558 – Some programmer dude Nov 18 '11 at 06:44
  • And yes, you are right in your interpretation that Pyhton changes its interpretation of "x" depending on whether there is an assignment to it in the function body. As for an answer, as Michael Hoffman puts it, in Python 3 you can use the "non-local" statement. – jsbueno Nov 18 '11 at 11:24
  • Great, thanks. I did search for similar questions, didn't find that one. – Steve Bennett Nov 21 '11 at 02:13

1 Answers1

6

The nonlocal statement in Python 3 will do this.


Edit: In Python 2, there's not a simple way to do it. I suggest you use some mutable container object if you need this capability. For example:

def a():
    def b():
        print d["x"]
        d["x"]=2
    d = dict(x=1)
    b()
    print d["x"]

If you absolutely must emulate nonlocal for CPython 2, you can hack it with the Python C API this way:

import ctypes
import inspect

locals_to_fast = ctypes.pythonapi.PyFrame_LocalsToFast
locals_to_fast.restype = None
locals_to_fast.argtypes = [ctypes.py_object, ctypes.c_int]

def set_in_frame(frame, name, value):
    frame.f_locals[name] = value
    locals_to_fast(frame, 1)

def a():
    def b(frame=inspect.currentframe()):
        print x
        set_in_frame(frame, "x", 2)
    x = 1
    b()
    print x

You could also set the frame local, and instead of calling PyFrame_LocalsToFast(), you could manipulate the bytecode of a so that it uses LOAD_NAME instead of LOAD_FAST. Please don't do either of these things. There is surely a better solution for your use case.

Michael Hoffman
  • 32,526
  • 7
  • 64
  • 86
  • ...and by implication, there is nothing in Python 2.x that allows it. – Steve Bennett Nov 21 '11 at 02:20
  • Not easily! Although there are a few ways to do it of varying hackiness. See my edit above. – Michael Hoffman Nov 21 '11 at 04:49
  • wow, nice edit :) I see the concept of what counts as assigning to a variable is a bit subtle. In my case, there really wasn't any desperate need to do any of these things (the outer function didn't actually need the value modified), but it's useful to know. – Steve Bennett Nov 21 '11 at 23:06