¿Generador de python Singleton? O, pickle un generador de python?

Estoy usando el siguiente código, con generadores nesteds, para iterar sobre un documento de texto y devolver ejemplos de capacitación usando get_train_minibatch() . Me gustaría persistir (encurtir) los generadores, para poder volver al mismo lugar en el documento de texto. Sin embargo, no se pueden encoger los generadores.

[editar: Dos ideas más:

]

Aquí está el código:

 def get_train_example(): for l in open(HYPERPARAMETERS["TRAIN_SENTENCES"]): prevwords = [] for w in string.split(l): w = string.strip(w) id = None prevwords.append(wordmap.id(w)) if len(prevwords) >= HYPERPARAMETERS["WINDOW_SIZE"]: yield prevwords[-HYPERPARAMETERS["WINDOW_SIZE"]:] def get_train_minibatch(): minibatch = [] for e in get_train_example(): minibatch.append(e) if len(minibatch) >= HYPERPARAMETERS["MINIBATCH SIZE"]: assert len(minibatch) == HYPERPARAMETERS["MINIBATCH SIZE"] yield minibatch minibatch = [] 

El siguiente código debe hacer más o menos lo que quieras. La primera clase define algo que actúa como un archivo pero puede ser decapado. (Cuando lo deselecciona, vuelve a abrir el archivo y busca la ubicación donde estaba cuando lo guardó). La segunda clase es un iterador que genera ventanas de palabras.

 class PickleableFile(object): def __init__(self, filename, mode='rb'): self.filename = filename self.mode = mode self.file = open(filename, mode) def __getstate__(self): state = dict(filename=self.filename, mode=self.mode, closed=self.file.closed) if not self.file.closed: state['filepos'] = self.file.tell() return state def __setstate__(self, state): self.filename = state['filename'] self.mode = state['mode'] self.file = open(self.filename, self.mode) if state['closed']: self.file.close() else: self.file.seek(state['filepos']) def __getattr__(self, attr): return getattr(self.file, attr) class WordWindowReader: def __init__(self, filenames, window_size): self.filenames = filenames self.window_size = window_size self.filenum = 0 self.stream = None self.filepos = 0 self.prevwords = [] self.current_line = [] def __iter__(self): return self def next(self): # Read through files until we have a non-empty current line. while not self.current_line: if self.stream is None: if self.filenum >= len(self.filenames): raise StopIteration else: self.stream = PickleableFile(self.filenames[self.filenum]) self.stream.seek(self.filepos) self.prevwords = [] line = self.stream.readline() self.filepos = self.stream.tell() if line == '': # End of file. self.stream = None self.filenum += 1 self.filepos = 0 else: # Reverse line so we can pop off words. self.current_line = line.split()[::-1] # Get the first word of the current line, and add it to # prevwords. Truncate prevwords when necessary. word = self.current_line.pop() self.prevwords.append(word) if len(self.prevwords) > self.window_size: self.prevwords = self.prevwords[-self.window_size:] # If we have enough words, then return a word window; # otherwise, go on to the next word. if len(self.prevwords) == self.window_size: return self.prevwords else: return self.next() 

Puede crear un objeto iterador estándar, simplemente no será tan conveniente como el generador; debe almacenar el estado del iterador en la instancia (para que esté decapado) y definir una función next () para devolver el siguiente objeto:

 class TrainExampleIterator (object): def __init__(self): # set up internal state here pass def next(self): # return next item here pass 

El protocolo del iterador es tan simple como eso, definir todo el método .next() en un objeto es todo lo que necesita para pasarlo a bucles, etc.

En Python 3, el protocolo de iterador usa el método __next__ lugar (algo más consistente).

Puede que esta no sea una opción para usted, pero Stackless Python ( http://stackless.com ) le permite encerrar cosas como funciones y generadores, bajo ciertas condiciones. Esto funcionará:

En foo.py:

 def foo(): with open('foo.txt') as fi: buffer = fi.read() del fi for line in buffer.split('\n'): yield line 

En foo.txt:

 line1 line2 line3 line4 line5 

En el intérprete:

 Python 2.6 Stackless 3.1b3 060516 (python-2.6:66737:66749M, Oct 2 2008, 18:31:31) IPython 0.9.1 -- An enhanced Interactive Python. In [1]: import foo In [2]: g = foo.foo() In [3]: g.next() Out[3]: 'line1' In [4]: import pickle In [5]: p = pickle.dumps(g) In [6]: g2 = pickle.loads(p) In [7]: g2.next() Out[7]: 'line2' 

Algunas cosas a tener en cuenta: debe almacenar el contenido del archivo y eliminar el objeto del archivo. Esto significa que el contenido del archivo se duplicará en el encurtido.

  1. Convierta el generador a una clase en la que el código del generador sea el método __iter__
  2. Agregue los métodos __getstate__ y __setstate__ a la clase, para manejar el decapado. Recuerda que no puedes decapar objetos de archivo. Entonces __setstate__ tendrá que volver a abrir los archivos, según sea necesario.

Describo este método con más profundidad, con código de ejemplo, aquí .

Puedes intentar crear un objeto llamable:

 class TrainExampleGenerator: def __call__(self): for l in open(HYPERPARAMETERS["TRAIN_SENTENCES"]): prevwords = [] for w in string.split(l): w = string.strip(w) id = None prevwords.append(wordmap.id(w)) if len(prevwords) >= HYPERPARAMETERS["WINDOW_SIZE"]: yield prevwords[-HYPERPARAMETERS["WINDOW_SIZE"]:] get_train_example = TrainExampleGenerator() 

Ahora puede convertir todos los estados que deben guardarse en campos de objeto y exponerlos a pickle. Esta es una idea básica y espero que esto ayude, pero todavía no lo he probado.

ACTUALIZAR:
Lamentablemente, no pude entregar mi idea. El ejemplo proporcionado no es una solución completa. Usted ve, TrainExampleGenerator no tiene estado. Debes diseñar este estado y dejarlo disponible para el decapado. Y el método __call__ debe usar y modificar ese estado para devolver el generador que comenzó desde la posición determinada por el estado del objeto. Obviamente, el generador en sí no será capaz de decaparse. Pero TrainExampleGenerator será posible para decapar y usted podrá recrear el generador con él como si el generador fuera decapado.