Lonnie Princehouse wrote: >> What's your use case exactly ? > > I'm trying to use a function to implicitly update a dictionary. The > whole point is to avoid the normal dictionary semantics, so kw['x'] = 5 > unfortunately won't do. > > I think bytecode hacks may be the way to go > I once messed around with something like that:
#----------- def_hacks.py """Silly operations on bytecode. Just for fun""" from dis import HAVE_ARGUMENT, opmap from types import CodeType as code def gendis(co): """Yield atomic operations from bytecode""" coiter = iter(co) for char in coiter: op = ord(char) if op >= HAVE_ARGUMENT: yield [op, ord(coiter.next()), ord(coiter.next())] else: yield [op] def thunk(func): """Function decorator -> code object. Hacks func.func_code so that it uses a real local dictionary rather than optimized locals. Returns a code object that can be exec'd in a context. This amounts to `exec function_body_source in context`, but does not requre accesses to the source""" out = [] c= func.func_code codestring = c.co_code # These may not be all the necessary hacks! replace_map = { opmap["STORE_FAST"]: opmap["STORE_NAME"], opmap["LOAD_FAST"]: opmap["LOAD_NAME"], opmap["DELETE_FAST"]: opmap["DELETE_NAME"], } names_list = list(c.co_names) # optimized locals are indexed in co_varnames # non-locals are indexed in co_names # so when we switch operations, we have to change the # index variables too name_map = dict((ix, names_list.index(name)) for ix, name in enumerate(c.co_varnames) if name in names_list) for atom in gendis(codestring): opcode = atom[0] if opcode in replace_map: atom[0] = replace_map[opcode] varindex = atom[1] + 256 * atom[2] atom[1:] = reversed(divmod(name_map[varindex], 256)) out.append("".join(chr(byte) for byte in atom)) codestring = "".join(out) # Make a new code object, using most of the properties of the original # but with new codestring, no arguments, and with flags adjusted return code(0, #c.co_argcount c.co_nlocals, c.co_stacksize, c.co_flags-3, codestring, c.co_consts, c.co_names, c.co_varnames, c.co_filename, c.co_name, c.co_firstlineno, c.co_lnotab, c.co_freevars, c.co_cellvars) @thunk def simple_test(): a = 4 b = 2 return c*a+b Then usage is: >>> d = {"__builtins__":None, "c":10} >>> eval(simple_test, d) 42 >>> d {'__builtins__': None, 'a': 4, 'c': 10, 'b': 2} >>> Use at your peril! Michael -- http://mail.python.org/mailman/listinfo/python-list