exec() bytecode with arbitrary locals?
Question: Suppose I want to execute code, for example

    
    
        value += 5
    

inside a namespace of my own (so the result is essentially `mydict['value'] +=
5`). There's a function `exec()`, but I have to pass a string there:

    
    
        exec('value += 5', mydict)
    

and passing statements as strings seems strange (e.g. it's not colorized that
way). Can it be done like:

    
    
        def block():
            value += 5
    
        ???(block, mydict)
    

? The obvious candidate for last line was `exec(block.__code__, mydict)`, but
no luck: it raises `UnboundLocalError` about `value`. I believe it basically
executes `block()`, not _the code inside block_ , so assignments aren't easy –
is that correct?

Of course, another possible solution would be to disassembly
`block.__code__`...

FYI, I got the question because of [this
thread](http://mail.python.org/pipermail/python-
ideas/2009-August/005552.html). Also, this is why some (me undecided) call for
new syntax

    
    
        using mydict: 
            value += 5
    

* * *

Note how this doesn't throw error but doesn't change `mydict` either:

    
    
        def block(value = 0):
            value += 5
    
        block(**mydict)
    


Answer: You can pass bytecode instead of a string to `exec`, you just need to make the
right bytecode for the purpose:

    
    
    >>> bytecode = compile('value += 5', '<string>', 'exec')
    >>> mydict = {'value': 23}
    >>> exec(bytecode, mydict)
    >>> mydict['value']
    28
    

Specifically, ...:

    
    
    >>> import dis
    >>> dis.dis(bytecode)
      1           0 LOAD_NAME                0 (value)
                  3 LOAD_CONST               0 (5)
                  6 INPLACE_ADD         
                  7 STORE_NAME               0 (value)
                 10 LOAD_CONST               1 (None)
                 13 RETURN_VALUE
    

the load and store instructions must be of the _NAME persuasion, and this
`compile` makes them so, while...:

    
    
    >>> def f(): value += 5
    ... 
    >>> dis.dis(f.func_code)
      1           0 LOAD_FAST                0 (value)
                  3 LOAD_CONST               1 (5)
                  6 INPLACE_ADD         
                  7 STORE_FAST               0 (value)
                 10 LOAD_CONST               0 (None)
                 13 RETURN_VALUE
    

...code in a function is optimized to use the _FAST versions, and those don't
work on a dict passed to `exec`. If you started somehow with a bytecode using
the _FAST instructions, you could patch it to use the _NAME kind instead, e.g.
with [bytecodehacks](http://mail.python.org/pipermail/python-announce-
list/2004-July/003280.html) or some similar approach.

