Lassen Sie uns eine Klasse haben, die Funktion hat, die von Zeit zu Zeit fehlschlägt, aber nach einigen Aktionen funktioniert es einfach perfekt.Generator Erholung mit Dekorator
Beispiel für ein echtes Leben wäre Mysql Abfrage, die _mysql_exceptions.OperationalError: (2006, 'MySQL server has gone away')
erhöht, aber nach Client Reconnection funktioniert es gut.
Ich habe versucht, Dekorateur für diese zu schreiben:
def _auto_reconnect_wrapper(func):
''' Tries to reconnects dead connection
'''
def inner(self, *args, _retry=True, **kwargs):
try:
return func(self, *args, **kwargs)
except Mysql.My.OperationalError as e:
# No retry? Rethrow
if not _retry:
raise
# Handle server connection errors only
# http://dev.mysql.com/doc/refman/5.0/en/error-messages-client.html
if (e.code < 2000) or (e.code > 2055):
raise
# Reconnect
self.connection.reconnect()
# Retry
return inner(self, *args, _retry=False, **kwargs)
return inner
class A(object):
...
@_auto_reconnect_wrapper
def get_data(self):
sql = '...'
return self.connection.fetch_rows(sql)
Und wenn Client-Verbindung es glücklich, nur leise wieder und jeder ist verliert.
Aber was, wenn ich get_data()
zum Generator (und verwenden yield
Anweisung) zu transformieren will:
@_auto_reconnect_wrapper
def get_data(self):
sql = '...'
cursor = self.connection.execute(sql)
for row in cursor:
yield row
cursor.close()
Nun, vorheriges Beispiel wird nicht funktionieren, weil innere Funktion bereits zurück Generator, und es bricht nach dem ersten Aufruf next()
.
Wie ich es verstehen, wenn Python yield
Innere Methode sieht sie die Steuerung nur unmittelbar ergibt (ohne eine einzige Anweisung ausführen) und wartet auf erste next()
.
Ich habe es geschafft, um es durch den Austausch funktioniert:
return func(self, *args, **kwargs)
mit:
for row in func(self, *args, **kwargs):
yield row
Aber ich bin gespannt, ob es elegantere (mehr pythonic) Weg, dies zu tun. Gibt es eine Möglichkeit, Python den gesamten Code bis zu yield
und dann laufen zu lassen?
Ich kenne die Möglichkeit, nur return tuple(func(self, *args, **kwargs))
aufrufen, aber ich möchte vermeiden, alle Datensätze auf einmal zu laden.
Ich mag diese Antwort (+ 1-ed), aber wie wäre es mit dem Fall, wenn 'next (gen) 'erhöht' StopIteration'? – Vyktor
@Vyktor Ich habe meine Antwort bearbeitet, um diesen Fall zu behandeln. Sie können nur diese Ausnahme abfangen und das Generator-Objekt zurückgeben, das keine Operation ausführt, wenn ein Versuch unternommen wird, darüber zu iterieren (oder "StopIteration" erneut zu erhöhen, wenn "nächste" aufgerufen wird). – dano
Ehrlich gesagt, das ist der Grund, warum ich vorgeschlagen habe, vor der Schleife in 'get_data' noch einen' yield' hinzuzufügen. Die Lösung des Dano ist jedoch viel klarer. – x3al