720 Stimmen

Wofür wird die "yield from"-Syntax in Python 3.3 in der Praxis hauptsächlich verwendet?

Es fällt mir schwer, das zu begreifen. PEP 380 .

  1. Welche Situationen gibt es, in denen yield from nützlich ist?
  2. Was ist der klassische Anwendungsfall?
  3. Warum wird es mit Mikrofäden verglichen?

Bisher habe ich Generatoren verwendet, aber nie wirklich Coroutines (eingeführt von PEP-342 ). Trotz einiger Ähnlichkeiten handelt es sich bei Generatoren und Coroutines um zwei grundsätzlich unterschiedliche Konzepte. Das Verständnis von Coroutines (nicht nur von Generatoren) ist der Schlüssel zum Verständnis der neuen Syntax.

IMHO Coroutines sind die obskurste Python-Funktion In den meisten Büchern wird es als nutzlos und uninteressant dargestellt.


Vielen Dank für die tollen Antworten, aber besonderen Dank an agf und sein Kommentar mit einem Link zu David Beazley Vorträge .

12voto

Jochen Ritzel Punkte 99416

yield from verkettet grundsätzlich Iteratoren auf effiziente Weise:

# chain from itertools:
def chain(*iters):
    for it in iters:
        for item in it:
            yield item

# with the new keyword
def chain(*iters):
    for it in iters:
        yield from it

Wie Sie sehen können, wird eine reine Python-Schleife entfernt. Das ist so ziemlich alles, was sie tut, aber die Verkettung von Iteratoren ist ein ziemlich häufiges Muster in Python.

Threads sind im Grunde eine Funktion, die es Ihnen ermöglicht, an völlig zufälligen Stellen aus Funktionen herauszuspringen und in den Zustand einer anderen Funktion zurückzuspringen. Der Thread-Supervisor tut dies sehr oft, so dass es so aussieht, als würde das Programm alle diese Funktionen gleichzeitig ausführen. Das Problem besteht darin, dass die Punkte zufällig sind, so dass Sie Sperren verwenden müssen, um zu verhindern, dass der Supervisor die Funktion an einem problematischen Punkt anhält.

Generatoren sind in diesem Sinne den Threads ziemlich ähnlich: Sie ermöglichen die Angabe bestimmter Punkte (wann immer sie yield ), wo man ein- und aussteigen kann. Wenn sie auf diese Weise verwendet werden, nennt man Generatoren Coroutines.

Lesen Sie dieses ausgezeichnete Tutorial über Coroutines in Python für weitere Details

11voto

Yeo Punkte 10530

In der angewandten Verwendung für die Asynchrone IO-Koroutine , yield from hat ein ähnliches Verhalten wie await in einem koroutine Funktion . Beide werden verwendet, um die Ausführung der Coroutine auszusetzen.

Für Asyncio, wenn keine Notwendigkeit besteht, eine ältere Python-Version (d.h. >3.5) zu unterstützen, async def / await ist die empfohlene Syntax für die Definition einer Coroutine. So yield from wird in einer Coroutine nicht mehr benötigt.

Aber im Allgemeinen außerhalb von asyncio, yield from <sub-generator> hat noch eine andere Verwendung bei der Iteration der Sub-Generator wie in der früheren Antwort erwähnt.

5voto

DomQ Punkte 3805

Einfach ausgedrückt, yield from bietet Schwanzrekursion für Iteratorfunktionen.

4voto

jimifiki Punkte 5142

Dieser Code definiert eine Funktion fixed_sum_digits Rückgabe eines Generators, der alle sechsstelligen Zahlen so aufzählt, dass die Summe der Ziffern 20 beträgt.

def iter_fun(sum, deepness, myString, Total):
    if deepness == 0:
        if sum == Total:
            yield myString
    else:  
        for i in range(min(10, Total - sum + 1)):
            yield from iter_fun(sum + i,deepness - 1,myString + str(i),Total)

def fixed_sum_digits(digits, Tot):
    return iter_fun(0,digits,"",Tot) 

Versuchen Sie, es ohne yield from . Wenn Sie eine wirksame Methode dafür finden, lassen Sie es mich wissen.

Ich denke, dass in Fällen wie diesem: der Besuch von Bäumen, yield from macht den Code einfacher und sauberer.

2voto

Yihua Zhou Punkte 128

yield from Erträge aus einem Generator, bis der Generator leer ist, und dann folgende Codezeilen weiter ausführen .

z.B..

def gen(sequence):
    for i in sequence:
        yield i

def merge_batch(sub_seq):
    yield {"data": sub_seq}

def modified_gen(g, batch_size):
    stream = []
    for i in g:
        stream.append(i)
        stream_len = len(stream)
        if stream_len == batch_size:
            yield from merge_batch(stream)
            print("batch ends")
            stream = []
            stream_len = 0

Dies gibt Ihnen die Möglichkeit zu laufen:

In [17]: g = gen([1,2,3,4,5,6,7,8,9,10])
In [18]: mg = modified_gen(g, 2)
In [19]: next(mg)
Out[19]: {'data': [1, 2]}

In [20]: next(mg)
batch ends
Out[20]: {'data': [3, 4]}

In [21]: next(mg)
batch ends
Out[21]: {'data': [5, 6]}

In [22]: next(mg)
batch ends
Out[22]: {'data': [7, 8]}

In [23]: next(mg)
batch ends
Out[23]: {'data': [9, 10]}

In [24]: next(mg)
batch ends
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
Input In [24], in <cell line: 1>()
----> 1 next(mg)

StopIteration: 

Also, Ertrag aus der Dose Ausgaben von einem anderen Generator übernehmen , einige Änderungen vornehmen und dann seine eigene Leistung als Generator selbst in andere einspeisen .

Das ist meiner bescheidenen Meinung nach einer der wichtigsten Anwendungsfälle für yield from

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X