Heim  >  Artikel  >  Backend-Entwicklung  >  Welche Funktion hat das Schlüsselwort yield in Python?

Welche Funktion hat das Schlüsselwort yield in Python?

silencement
silencementOriginal
2019-05-23 11:28:309609Durchsuche

Die Rolle des yield-Schlüsselworts: 1. Durch die Verwendung des Generators können Systemressourcen effektiv gespart und unnötiger Speicherverbrauch vermieden werden. 3. Coroutine 4. Arbeiten Sie mit from zusammen, um yield from zu bilden, das zum Konsumieren von Subgeneratoren und zum Übermitteln von Nachrichten verwendet wird.

Welche Funktion hat das Schlüsselwort yield in Python?

Die Verwendung von Yield hat die folgenden vier häufigen Situationen:

  • Einer ist ein Generator,

    Um es zusammenzufassen: Der Code im Generator kehrt zurück, wenn er Yield erreicht, und der zurückgegebene Inhalt ist der Ausdruck nach Yield. Wenn der interne Code des Generators das nächste Mal ausgeführt wird, wird er im letzten Zustand fortgesetzt. Mit dem Schlüsselwort yield können wir eine Funktion ganz einfach in einen Generator umwandeln.

  • Der zweite wird verwendet, um den Kontextmanager zu definieren,

  • Der dritte ist die Coroutine,

  • Die vierte besteht darin, mit from zusammenzuarbeiten, um einen Ertrag von zu bilden, der zum Konsumieren von Subgeneratoren und zum Übermitteln von Nachrichten verwendet wird.

Diese vier Verwendungen leiten sich tatsächlich von der Pausenfunktion von yield ab. Das heißt, wenn das Programm an der Stelle ausgeführt wird, an der yield = yield expr ist, führt es zuerst yield expr aus Geben Sie den resultierenden Wert an den Aufrufer zurück, der den Generator aufgerufen hat, und warten Sie dann, bis der Aufrufer erneut aktiviert und die Ausführung des Programms fortsetzt. Abhängig von der vom Wiederherstellungsprogramm verwendeten Methode ändert sich auch der Ergebniswert des Yield-Ausdrucks entsprechend. Bei einem Aufruf mit __next()__ ist der Wert des Yield-Ausdrucks „Ergebnis“ „None“; bei einem Aufruf mit „send()“ ist der Wert des Yield-Ausdrucks „Ergebnis“ der durch die Sendefunktion übergebene Wert. Das Folgende ist ein Beispiel für den im offiziellen Dokument [1] eingeführten Yield-Ausdruck, der die Eigenschaften und Verwendung des Schlüsselworts Yield gut veranschaulichen kann:

>>> def echo(value=None):
...     print("Begin...")
...     try:
...         while True:
...             try:
...                 value = (yield value)
...             except Exception as e:
...                 value = e
...     finally:
...         print("Clean up!!!")
...
>>> generator = echo(1)
>>> print(next(generator))
Begin...
1
>>> print(next(generator))
None
>>> print(generator.send(2))
2
>>> generator.throw(TypeError, "spam")
TypeError('spam')
>>> generator.close()
Clean up!!!

Die Beschreibung des obigen Codes lautet wie folgt:

Welche Funktion hat das Schlüsselwort yield in Python?

  • Bei der Ausführung des ersten Next(Generators), also der Voraktivierung des Generators, beginnt der Generator mit der Ausführung und gibt die Begin...-Zeichenfolge aus und wird ausgeführt. Wenn Sie die Position von value = (Yield Value) erreichen, rufen Sie zuerst Yield Value auf, um die Zahl 1 zu generieren, und dann hält der Generator an der Position von Yield an.

  • Wenn der zweite next(generator) aufgerufen wird, nimmt der Generator die Ausführung wieder auf. Da next() zum Aufrufen der Generatorfunktion verwendet wird, wird der Wert von value zu None, also ist es so generiert Wenn die Interpreterfunktion die Ausführung fortsetzt, um einen Wert zu liefern, wird der Wert von value None an den Interpreter zurückgegeben und dann erneut angehalten.

  • Verwenden Sie dann die Methode send(2), um den Generator weiter aufzurufen. Der Wert empfängt die eingehende Nummer 2, führt weiterhin value = (Ertragswert) aus und gibt die Nummer 2 an den zurück Dolmetscher dann Pause.

  • Danach ruft der Interpreter die Methode throw(TypeError, „spam“) erneut auf und löst eine Ausnahme aus ('spam') wird dem Variablenwert zugewiesen, und dann wird das Programm erneut ausgeführt, um value = (Ertragswert) zu erhalten, und TypeError('spam') wird an den Interpreter zurückgegeben.

  • Schließlich ruft das Programm die Methode close () auf, löst GeneratorExit an der Position der Generatorfunktion aus, löst eine Ausnahme aus, der Generator wird normal beendet und führt schließlich die äußerste try-Anweisung aus entsprechend Der letzte Zweig druckt Aufräumen.

Es gibt in Python eine sehr nützliche Syntax namens Generator, und das verwendete Schlüsselwort ist yield. Durch den effektiven Einsatz des Generator-Tools können Systemressourcen effektiv gespart und unnötige Speichernutzung vermieden werden.

Generator

Wie erwartet wird sich Yield zum ersten Mal definitiv in einer Generatorfunktion befinden. Ein Generator ist eine Funktion, die kontinuierlich Zahlen oder andere Arten von Werten generiert. Er kann einzeln über eine for-Schleife oder die Funktion next() aufgerufen werden. Hervorzuheben ist hier, dass der Generator einen Ertragsausdruck ohne Zuweisung enthält, sodass die folgenden beiden Formen äquivalent sind [2]:

def integers_1():
    for i in range(4):
        yield i + 1def integers_2():
    for i in range(4):
        value = yield i + 1

Der Grund, warum die zweite Form hier hervorgehoben wird, ist, dass der Ertrag besser sein kann verstanden, wenn man das Senden von Werten über die send()-Methode versteht. Gleichzeitig kann auch korrekter erklärt werden, dass der durch den Aufruf des Generators zurückgegebene Wert der Wert des Ausdrucks i + 1 auf der rechten Seite des Yield-Schlüsselworts ist und nicht der Ergebniswert des Yield-Ausdrucks selbst.

Versuchen wir aufzurufen:

>>> for n in integers_1():
...     print(n)
...
1
2
3
4
>>> for n in integers_2():
...     print(n)
...
1
2
3
4

Kontextmanager

Mit dem @contextmanager-Dekorator im Contexlib-Modul von Python kann yield auch zum Definieren des Kontexts verwendet werden Managern ist das Folgende ein Beispiel aus dem Python Tricks-Buch [3]:

from contextlib import contextmanager

@contextmanager
def managed_file(name):
    try:
        f = open(name, 'w')
        yield f
    finally:
        f.close()

Der oben durch das Schlüsselwort decorator und yield definierte Kontextmanager entspricht der Methodendefinition der folgenden Klasse:

class ManagedFile:
    def __init__(self, name):
        self.name = name
    def __enter__(self):
        self.file = open(self.name, 'w')
        return self.file
    def __exit__(self, exc_type, exc_val, exc_tb):
        if self.file:
            self.file.close()

kann mit den folgenden Methoden separat aufgerufen werden:

>>> with ManagedFile('hello.txt') as f:
...     f.write('hello, world!')
...     f.write('bye now')

>>> with managed_file('hello.txt') as f:
...     f.write('hello, world!')
...     f.write('bye now')

Coroutine

Das Konzept von Coroutine ist voller Schönheit und stimmt immer noch sehr gut mit den Arbeitsmustern der Menschen überein Es erfordert einige Anstrengung, es vollständig zu beherrschen. Aber der Aufwand lohnt sich, denn manchmal kann Multithreading weitaus mehr Ärger verursachen als Coroutinen. Das Folgende ist ein Beispiel für eine Coroutine, die ausschließlich unter Verwendung von Yield-Ausdrücken aus dem Python Cookbook [4] geschrieben wurde: ​​

from collections import deque

# Two simple generator functions
def countdown(n):
    while n > 0:
        print('T-minus', n)
        yield
        n -= 1
    print('Blastoff!')

def countup(n):
    x = 0
    while x < n:
        print(&#39;Counting up&#39;, x)
        yield
        x += 1

class TaskScheduler:
    def __init__(self):
        self._task_queue = deque()

    def new_task(self, task):
        &#39;&#39;&#39;
        Admit a newly started task to the scheduler

        &#39;&#39;&#39;
        self._task_queue.append(task)

    def run(self):
        &#39;&#39;&#39;
        Run until there are no more tasks
        &#39;&#39;&#39;
        while self._task_queue:
            task = self._task_queue.popleft()
            try:
                # Run until the next yield statement
                next(task)
                self._task_queue.append(task)
            except StopIteration:
                # Generator is no longer executing
                pass

# Example use
sched = TaskScheduler()
sched.new_task(countdown(2))
sched.new_task(countup(5))
sched.run()

运行上面的脚本,可以得到以下输出:

T-minus 2
Counting up 0
T-minus 1
Counting up 1
Blastoff!
Counting up 2
Counting up 3
Counting up 4

countdown 和 countup 两个任务交替执行,主程序在执行到 countdown 函数的 yield 表达式时,暂停后将被重新附加到队列里面。然后,countup 任务从队列中取了出来,并开始执行到 yield 表达式的地方后暂停,同样将暂停后的协程附加到队列里面,接着从队列里取出最左边的任务 countdown 继续执行。重复上述过程,直到队列为空。

上面的协程可以利用 Python3.7 中的 asyncio 库改写为:

import asyncio

async def countdown(n):
    while n > 0:
        print(&#39;T-minus&#39;, n)
        await asyncio.sleep(0)
        n -= 1
    print(&#39;Blastoff!&#39;)

async def countup(n):
    x = 0
    while x < n:
        print(&#39;Counting up&#39;, x)
        await asyncio.sleep(0)
        x += 1

async def main():
    await asyncio.gather(countdown(2), countup(5))

asyncio.run(main())

可以看到利用 asyncio 库编写的协程示例比用 yield 来编写的协程要优雅地多,也简单地多,更容易被人理解。

yield from

说实话,yield from 实在有点令人费解,让人摸不着头脑。yield from 更多地被用于协程,而 await 关键字的引入会大大减少 yield from 的使用频率。yield from 一方面可以迭代地消耗生成器,另一方面则建立了一条双向通道,可以让调用者和子生成器便捷地通信,并自动地处理异常,接收子生成器返回的值。下面是 Python Cookbook 书里的一个例子,用于展开嵌套的序列[5]:

from collections.abc import Iterable

def flatten(items, ignore_types=(str, bytes)):
    for x in items:
        if isinstance(x, Iterable) and not isinstance(x, ignore_types):
            yield from flatten(x)
        else:
            yield x

items = [1, 2, [3, 4, [5, 6], 7], 8]
# Produces 1 2 3 4 5 6 7 8
for x in flatten(items):
    print(x)

而 yield from 用于建立双向通道的用法则可以参考 Fluent Python 里例子[6],这里就不详细地解释这段代码:

# BEGIN YIELD_FROM_AVERAGER
from collections import namedtuple

Result = namedtuple(&#39;Result&#39;, &#39;count average&#39;)


# the subgenerator
def averager():
    total = 0.0
    count = 0
    average = None
    while True:
        term = yield
        if term is None:
            break
        total += term
        count += 1
        average = total/count
    return Result(count, average)


# the delegating generator
def grouper(results, key):
    while True:
        results[key] = yield from averager()


# the client code, a.k.a. the caller
def main(data):
    results = {}
    for key, values in data.items():
        group = grouper(results, key)
        next(group)
        for value in values:
            group.send(value)
        group.send(None)

    report(results)


# output report
def report(results):
    for key, result in sorted(results.items()):
        group, unit = key.split(&#39;;&#39;)
        print(f&#39;{result.count:2} {group:5} averaging {result.average:.2f}{unit}&#39;)


data = {
    &#39;girls;kg&#39;:
        [40.9, 38.5, 44.3, 42.2, 45.2, 41.7, 44.5, 38.0, 40.6, 44.5],
    &#39;girls;m&#39;:
        [1.6, 1.51, 1.4, 1.3, 1.41, 1.39, 1.33, 1.46, 1.45, 1.43],
    &#39;boys;kg&#39;:
        [39.0, 40.8, 43.2, 40.8, 43.1, 38.6, 41.4, 40.6, 36.3],
    &#39;boys;m&#39;:
        [1.38, 1.5, 1.32, 1.25, 1.37, 1.48, 1.25, 1.49, 1.46],
}


if __name__ == &#39;__main__&#39;:
    main(data)

可能对于熟练掌握 Python 的程序员来说,yield 和 yield from 相关的语法充满了美感。但对于刚入门的我来说,除了生成器语法让我感觉到了美感,其他的语法都让我理解起来很是费解。不过还好,asyncio 库融入了 Python 的标准库里,关键字 async 和 await 的引入,将会让我们更少地在编写协程时去使用 yield 和 yield from。 但不管怎么样,yield 都是 Python 里非常特别的一个关键字,值得花时间好好掌握了解。

Das obige ist der detaillierte Inhalt vonWelche Funktion hat das Schlüsselwort yield in Python?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Vorheriger Artikel:So sortieren Sie Python-TupelNächster Artikel:So sortieren Sie Python-Tupel