Ich hatte unten einen Code in Python 3.2 und wollte ihn in Python 2.7 ausführen. Ich habe es konvertiert (habe den Code missing_elements
in beiden Versionen eingegeben), bin mir aber nicht sicher, ob dies der effizienteste Weg ist. Was passiert grundsätzlich, wenn zwei yield from
Aufrufe wie unten in der oberen und unteren Hälfte missing_element
funktionieren? Werden die Einträge aus den beiden Hälften (obere und untere) in einer Liste aneinander angehängt, sodass die übergeordnete Rekursion beim yield from
Aufruf funktioniert und beide Hälften zusammen verwendet werden?
def missing_elements(L, start, end): # Python 3.2
if end - start <= 1:
if L[end] - L[start] > 1:
yield from range(L[start] + 1, L[end])
return
index = start + (end - start) // 2
# is the lower half consecutive?
consecutive_low = L[index] == L[start] + (index - start)
if not consecutive_low:
yield from missing_elements(L, start, index)
# is the upper part consecutive?
consecutive_high = L[index] == L[end] - (end - index)
if not consecutive_high:
yield from missing_elements(L, index, end)
def main():
L = [10, 11, 13, 14, 15, 16, 17, 18, 20]
print(list(missing_elements(L, 0, len(L)-1)))
L = range(10, 21)
print(list(missing_elements(L, 0, len(L)-1)))
def missing_elements(L, start, end): # Python 2.7
return_list = []
if end - start <= 1:
if L[end] - L[start] > 1:
return range(L[start] + 1, L[end])
index = start + (end - start) // 2
# is the lower half consecutive?
consecutive_low = L[index] == L[start] + (index - start)
if not consecutive_low:
return_list.append(missing_elements(L, start, index))
# is the upper part consecutive?
consecutive_high = L[index] == L[end] - (end - index)
if not consecutive_high:
return_list.append(missing_elements(L, index, end))
return return_list
python
generator
python-2.x
yield
yield-from
vkaul11
quelle
quelle
Antworten:
Wenn Sie die Ergebnisse Ihrer Erträge nicht verwenden, * können Sie dies jederzeit ändern:
yield from foo
… das mögen:
for bar in foo: yield bar
Es kann Leistungskosten geben, ** aber es gibt nie einen semantischen Unterschied.
Nein! Der springende Punkt bei Iteratoren und Generatoren ist, dass Sie keine tatsächlichen Listen erstellen und diese zusammenfügen.
Aber der Effekt ist ähnlich: Sie geben nur von einem nach, dann von einem anderen.
Wenn Sie sich die obere und die untere Hälfte als "faule Listen" vorstellen, dann können Sie sich dies als "faulen Anhang" vorstellen, der eine größere "faule Liste" erstellt. Und wenn Sie rufen
list
auf das Ergebnis der Funktion Eltern, können Sie natürlich wird eine tatsächliche erhaltenlist
, die das Anhängen zusammen die beiden Listen gleichwertig ist Sie bekommen hätte , wenn Sie getan hatteyield list(…)
stattyield from …
.Aber ich denke, es ist einfacher, es anders herum zu sehen: Was es tut, ist genau das gleiche, was die
for
Schleifen tun.Wenn Sie die beiden Iteratoren in Variablen gespeichert und eine Schleife durchlaufen
itertools.chain(upper, lower)
hätten, wäre dies dasselbe wie eine Schleife über die erste und eine Schleife über die zweite, oder? Kein Unterschied hier. In der Tat könnten Sie wie folgt implementierenchain
:for arg in *args: yield from arg
* Nicht die Werte, die der Generator seinem Aufrufer liefert, sondern der Wert der Ertragsausdrücke selbst innerhalb des Generators (die vom Aufrufer mithilfe der
send
Methode stammen), wie in PEP 342 beschrieben . Sie verwenden diese in Ihren Beispielen nicht. Und ich bin bereit zu wetten, dass Sie nicht in Ihrem richtigen Code sind. Code im Coroutine-Stil verwendet jedoch häufig den Wert einesyield from
Ausdrucks - Beispiele finden Sie in PEP 3156 . Ein solcher Code hängt normalerweise von anderen Funktionen der Python 3.3-Generatoren ab, insbesondere von den neuen FunktionenStopIteration.value
des gleichen PEP 380 , die eingeführt wurdenyield from
- also muss es neu geschrieben werden. Wenn nicht, können Sie das PEP verwenden, das Ihnen auch das komplette schreckliche, unordentliche Äquivalent zeigt, und Sie können natürlich die Teile reduzieren, die Sie nicht interessieren. Wenn Sie den Wert des Ausdrucks nicht verwenden, wird er auf die beiden obigen Zeilen reduziert.** Keine große, und Sie können nichts dagegen tun, außer Python 3.3 zu verwenden oder Ihren Code vollständig umzustrukturieren. Dies ist genau der gleiche Fall wie das Übersetzen von Listenverständnissen in Python 1.5-Schleifen oder ein anderer Fall, in dem Version XY neu optimiert wurde und Sie eine ältere Version verwenden müssen.
quelle
yield
Ausdrücke innerhalb des Generators. Das ist schwer zu erklären; Siehe PEP 342, wenn Sie es verstehen wollen, aber kurz: Wenn Sie niemalssend
einen Generator anrufen oder dies niemalsfoo = (yield bar)
im Generator tun und sich nicht vorstellen können, warum Sie dies jemals tun möchten, machen Sie sich bis dahin keine Sorgen Sie haben Zeit, PEP 342 (und 380 und 3156 sowie Greg Ewings raffinierte Blog-Beiträge von 3156) zu lesen.done
und erhöhen dannStopIteration
. (Sie markieren auch sowohl die Funktion als Generator Funktion trotz nichts ergeben, dh Sie könnenyield from ()
stattif False: yield None
einen Generator zu erzwingen.)Ich bin gerade auf dieses Problem gestoßen und meine Verwendung war etwas schwieriger, da ich den Rückgabewert von
yield from
: benötigte.result = yield from other_gen()
Dies kann nicht als einfache
for
Schleife dargestellt werden, sondern kann folgendermaßen reproduziert werden:_iter = iter(other_gen()) try: while True: #broken by StopIteration yield next(_iter) except StopIteration as e: if e.args: result = e.args[0] else: result = None
Hoffentlich hilft dies Menschen, die auf das gleiche Problem stoßen. :) :)
quelle
Ersetzen Sie sie durch for-Schleifen:
yield from range(L[start] + 1, L[end]) ==> for i in range(L[start] + 1, L[end]): yield i
Das gleiche gilt für Elemente:
yield from missing_elements(L, index, end) ==> for el in missing_elements(L, index, end): yield el
quelle
Ich glaube, ich habe einen Weg gefunden, das Python 3.x-
yield from
Konstrukt in Python 2.x zu emulieren . Es ist nicht effizient und ein bisschen hackig, aber hier ist es:import types def inline_generators(fn): def inline(value): if isinstance(value, InlineGenerator): for x in value.wrapped: for y in inline(x): yield y else: yield value def wrapped(*args, **kwargs): result = fn(*args, **kwargs) if isinstance(result, types.GeneratorType): result = inline(_from(result)) return result return wrapped class InlineGenerator(object): def __init__(self, wrapped): self.wrapped = wrapped def _from(value): assert isinstance(value, types.GeneratorType) return InlineGenerator(value)
Verwendung:
@inline_generators def outer(x): def inner_inner(x): for x in range(1, x + 1): yield x def inner(x): for x in range(1, x + 1): yield _from(inner_inner(x)) for x in range(1, x + 1): yield _from(inner(x)) for x in outer(3): print x,
Erzeugt Ausgabe:
1 1 1 2 1 1 2 1 2 3
Vielleicht findet das jemand hilfreich.
Bekannte Probleme: Es fehlt die Unterstützung für send () und verschiedene in PEP 380 beschriebene Eckfälle. Diese können hinzugefügt werden, und ich werde meinen Eintrag bearbeiten, sobald er funktioniert.
quelle
From
Methode tut . Die Umsetzung ist sicherlich produktionsbereit.Wie wäre es mit der Definition von pep-380, um eine Python 2-Syntaxversion zu erstellen:
Die Aussage:
RESULT = yield from EXPR
ist semantisch äquivalent zu:
_i = iter(EXPR) try: _y = next(_i) except StopIteration as _e: _r = _e.value else: while 1: try: _s = yield _y except GeneratorExit as _e: try: _m = _i.close except AttributeError: pass else: _m() raise _e except BaseException as _e: _x = sys.exc_info() try: _m = _i.throw except AttributeError: raise _e else: try: _y = _m(*_x) except StopIteration as _e: _r = _e.value break else: try: if _s is None: _y = next(_i) else: _y = _i.send(_s) except StopIteration as _e: _r = _e.value break RESULT = _r
In einem Generator lautet die Anweisung:
return value
ist semantisch äquivalent zu
raise StopIteration(value)
mit der Ausnahme, dass die Ausnahme wie derzeit nicht durch
except
Klauseln im zurückkehrenden Generator abgefangen werden kann .Die StopIteration-Ausnahme verhält sich wie folgt definiert:
class StopIteration(Exception): def __init__(self, *args): if len(args) > 0: self.value = args[0] else: self.value = None Exception.__init__(self, *args)
quelle
Ich habe festgestellt, dass die Verwendung von Ressourcenkontexten (mithilfe des Python-Ressourcenmoduls ) ein eleganter Mechanismus für die Implementierung von Subgeneratoren in Python 2.7 ist. Praktischerweise hatte ich die Ressourcenkontexte sowieso schon verwendet.
Wenn Sie in Python 3.3 Folgendes hätten:
@resources.register_func def get_a_thing(type_of_thing): if type_of_thing is "A": yield from complicated_logic_for_handling_a() else: yield from complicated_logic_for_handling_b() def complicated_logic_for_handling_a(): a = expensive_setup_for_a() yield a expensive_tear_down_for_a() def complicated_logic_for_handling_b(): b = expensive_setup_for_b() yield b expensive_tear_down_for_b()
In Python 2.7 hätten Sie:
@resources.register_func def get_a_thing(type_of_thing): if type_of_thing is "A": with resources.complicated_logic_for_handling_a_ctx() as a: yield a else: with resources.complicated_logic_for_handling_b_ctx() as b: yield b @resources.register_func def complicated_logic_for_handling_a(): a = expensive_setup_for_a() yield a expensive_tear_down_for_a() @resources.register_func def complicated_logic_for_handling_b(): b = expensive_setup_for_b() yield b expensive_tear_down_for_b()
Beachten Sie, dass für die Operationen mit komplizierter Logik nur die Registrierung als Ressource erforderlich ist.
quelle
yield from
anderer Generator ist (genau einmal immer), können Sie stattdessen einfach diesen Generator zurückgeben.get_a_thing
könnte beideyield from
durch ein ersetzenreturn
und es würde genauso gut funktionieren.