Singleton Python-Generator? Oder Beize ein Python-Generator?
-
20-09-2019 - |
Frage
Ich verwende den folgenden Code, mit verschachtelten Generatoren, ein Textdokument iterieren und Rückkehr Trainingsbeispiele mit get_train_minibatch()
. Ich möchte die Generatoren bestehen bleiben (Gurke), so dass ich an der gleichen Stelle im Textdokument zurückbekommen kann. Sie können jedoch nicht Beize Generatoren.
-
Gibt es eine einfache Abhilfe, so dass ich meine Position speichern und starten Sie wieder da, wo ich aufgehört? Vielleicht kann ich
get_train_example()
ein Singleton machen, so dass ich herumliegen nicht mehrere Generatoren haben. Dann könnte ich eine globale Variable in diesem Modul machen, die verfolgen, wie weit hält entlangget_train_example()
ist. -
Haben Sie einen besseren (Reiniger) Vorschlag, mir zu erlauben, diesen Generator zu beharren?
[edit: Zwei weitere Ideen:
-
Kann ich eine Membervariable / Verfahren zum Generator hinzufügen, so dass ich generator.tell () aufrufen können und den Speicherort der Datei finden? Denn dann habe ich das nächste Mal den Generator erstellen, kann ich es fragen zu diesem Standort zu suchen. Diese Idee klingt das einfachste von allem.
-
Kann ich eine Klasse erstellen, und der Speicherort der Datei haben eine Membervariable sein, und haben dann der Generator innerhalb der Klasse erstellt und aktualisiert den Speicherort der Datei Membervariable jedes Mal, es ergibt? Denn dann kann ich wissen, wie weit in die Datei, um sie es.
]
Hier ist der Code:
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 = []
Lösung
Der folgende Code sollte tun mehr oder weniger das, was Sie wollen. Die erste Klasse definiert etwas, das wie eine Datei handelt, sondern kann gebeizt werden. (Wenn Sie es unpickle, es wieder öffnet die Datei und sucht nach dem Ort, wo es war, als Sie es gebeizt). Die zweite Klasse ist ein Iterator, Wort Fenster erzeugt.
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()
Andere Tipps
Sie können ein Standard-Iterator-Objekt erstellen, wird es einfach nicht so bequem sein, als der Generator; Sie müssen die Iterators Zustand auf dem instace speichern (so dass es gebeizt) und definieren eine nächste () Funktion das nächste Objekt zurück:
class TrainExampleIterator (object):
def __init__(self):
# set up internal state here
pass
def next(self):
# return next item here
pass
Das Iterator-Protokoll ist einfach, dass die Definition der .next()
Methode für ein Objekt ist alles, was Sie brauchen, um es zu passieren, um für Schleifen etc.
In Python 3, das Iterator-Protokoll verwendet die __next__
Methode statt (etwas konsequente).
Das kann keine Option für Sie sein, aber Stackless Python ( http://stackless.com ) funktioniert können Sie Dinge wie Funktionen und Generatoren beizen, unter bestimmten Bedingungen. Dies funktioniert:
In foo.py:
def foo():
with open('foo.txt') as fi:
buffer = fi.read()
del fi
for line in buffer.split('\n'):
yield line
In foo.txt:
line1
line2
line3
line4
line5
Im Interpreter:
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'
Einige Dinge zu beachten: Sie muss Puffer, um den Inhalt der Datei, und löschen Sie die Datei-Objekt. Dies bedeutet, dass der Inhalt der Datei wird in der Beize dupliziert werden.
Sie auch interessieren könnten NLTK Corpus Leser mit:
- http: //nltk.googlecode .com / svn / trunk / doc / api / nltk.corpus.reader-module.html
- http://nltk.googlecode.com/svn/trunk /doc/howto/corpus.html
-Edward
- Konvertieren des Generators in eine Klasse, in der der Generator-Code ist die
__iter__
Methode - Add
__getstate__
und__setstate__
Methoden der Klasse, Beizen Handhabung. Denken Sie daran, dass Sie nicht Dateiobjekte Beize kann. So__setstate__
wird wieder zu öffnen, Dateien müssen, falls erforderlich.
Ich beschreibe diese Methode in mehr Tiefe, mit Beispielcode, hier .
Sie können versuchen, aufrufbare Objekt erstellen:
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()
Jetzt können Sie alle Zustand drehen, dass der Bedarf in Objektfelder und setzen sie gerettet werden beizen. Dies ist eine grundlegende Idee und ich hoffe, das hilft, aber ich habe nicht das selbst noch nicht ausprobiert.
UPDATE:
Leider scheiterte, ich meine Idee zu liefern. Vorausgesetzt Beispiel ist nicht vollständige Lösung. Sie sehen, TrainExampleGenerator
keinen Staat haben. Sie müssen diesen Zustand entwickeln und zur Verfügung stellen zum Beizen. Und __call__
Methode soll diesen Zustand, so dass verwenden und ändert Generator zurückzukehren, die von der Position von Objektzustand bestimmt gestartet. Offensichtlich Generator selbst nicht Beize-fähig sein. Aber TrainExampleGenerator
möglich sein wird, beizen und Sie werden damit neu erstellen Generator in der Lage sein, als ob Generator selbst gebeizt wurde.