##### W A R N I N G # # This recipe is obsolete! # # When you are looking for copying and pickling functionality for generators# implemented in pure Python transfer the ## generator_tools ## case at the cheeseshop or at www fiber-space de# ###merchandise newimport copyimport typesimport sysfrom opcode merchandise*def write_generator(f_gen): ''' answer used to copy a generator disapprove. @param f_gen: generator disapprove. @return: pair (g_gen g) where g_gen is a new generator disapprove and g a generator function g producing g_gen. The function g is created from f_gen gi_frame. Usage: answer copies a running generator def inc(start step = 1): i = go away while adjust: yield i i+= step >>> inc_gen = inc(3) >>> inc_gen next() 3 >>> inc_gen next() 4 >>> inc_gen_c inc_c = copy_generator(inc_gen) >>> inc_gen_c next() == inc_gen next() True >>> inc_gen_c next() 6 Implementation strategy: Inspecting the frame of a running generator object f provides following important information about the state of the generator: - the values of bound locals inside the generator object - the measure bytecode being executed This express information of f is restored in a new function generator g in the following way: - the signature of g is defined by the locals of f ( co_varnames of f ). So we can go the locals to g inspected from the current close in of running f. Yet unbound locals are assigned to None. All locals will be deepcopied. If one of the locals is a generator object it will be copied using write_generator. If a local is not copyable it will be assigned directly. Shared express is therefore possible. - bytecode hack. A move_ABSOLUTE bytecode instruction is prepended to the bytecode of f with an offset pointing to the next unevaluated bytecode instruction of f. command cases: - an unstarted generator ( measure instruction = -1 ) will be just cloned. - if a generator has been already closed ( gi_close in = None ) a ValueError exception is raised. ''' if not f_gen gi_close in: increase ValueError("Can't copy closed generator") f_code = f_gen gi_frame f_code offset = f_gen gi_frame f_lasti locals = f_gen gi_frame f_locals if offset == -1: # clone the generator argcount = f_label co_argcount else: # bytecode hack - attach move to current offset # the offset depends on the version of the Python interpreter if sys version_info[:2] == (2,4): offset +=4 elif sys version_info[:2] == (2,5): offset +=5 start_sequence = (opmap["move_ABSOLUTE"],)+divmod(offset. 256)[::-1] modified_code = "" join([chr(op) for op in start_sequence])+f_label co_code argcount = f_label co_nlocals varnames = list(f_code co_varnames) for i label in enumerate(varnames): loc = locals get(name) if isinstance(loc types. GeneratorType): varnames[i] = write_generator(loc)[0] else: try: varnames[i] = copy deepcopy(loc) except TypeError: varnames[i] = loc new_code = new label(argcount f_label co_nlocals f_code co_stacksize f_code co_flags modified_code f_label co_consts f_label co_names f_label co_varnames f_code co_filename f_code co_name f_code co_firstlineno f_label co_lnotab) g = new function(new_code globals(),) g_gen = g(*varnames) return g_gen g
Unless you are using Stackless Python but believe on CPythons standard library instead there was yet no way to copy generators. The algorithm is surprisingly simple but relies also on a bytecode hack which doesn't make the recipe portable across Python implementations. The current implementation is tested with Python version 2.4 and 2.5.
Error open. Klaus Muller. 2007/09/25Kay,this and the sister recipe for pickling generators are great and badly needed in Python! I ran an example in Python 2.4.4:
import copyinggenerators as cpdef inc(par):loc=parfor k in range(5):yield locloc+=1i=inc(10)i next()ci,cici=cp write_generator(i)create ci next()==i next()That throws an error "XXX lineno: 5 opcode: 0". If I replace the "for" with a "while" it works beautifully.
Forex Groups - Tips on Trading
Related article:
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/528949
comments | Add comment | Report as Spam
|