>백엔드 개발 >파이썬 튜토리얼 >Python에서 Yield 키워드의 기능은 무엇입니까?

Python에서 Yield 키워드의 기능은 무엇입니까?

silencement
silencement원래의
2019-05-23 11:28:309732검색

Python에서 Yield 키워드의 기능은 다음과 같습니다. 1. 생성기를 사용하면 시스템 리소스를 효과적으로 절약하고 불필요한 메모리 사용을 피할 수 있습니다. 3. 협업 프로세스를 정의하는 데 사용됩니다. from과 협력하여 하위 생성기를 소비하고 메시지를 전달하는 데 사용되는 항복을 형성합니다.

Python에서 Yield 키워드의 기능은 무엇입니까?

yield는 다음과 같은 네 가지 일반적인 상황에서 사용됩니다.

  • 하나는 생성기입니다.

    요약하자면 생성기 내부의 코드는 Yield에 도달하면 반환되며 반환되는 내용은 Yield 표현식 이후입니다. . 다음에 생성기의 내부 코드가 실행될 때 마지막 상태부터 계속됩니다. Yield 키워드를 통해 함수를 생성기로 쉽게 수정할 수 있습니다.

  • 두 번째는 컨텍스트 관리자를 정의하는 데 사용됩니다.

  • 세 번째는 코루틴입니다.

  • 네 번째는 from과 함께 사용되어 하위 생성기를 소비하는 데 사용되는 Yield를 형성합니다. 메시지를 전달합니다.

이 네 가지 사용법은 실제로 Yield의 일시 중지 기능에서 파생됩니다. 즉, 프로그램이 Yield가 결과 = Yield expr인 위치로 실행되면 먼저 Yield expr을 실행하고 생성된 값을 호출에 반환합니다. 생성기를 호출한 다음 일시 중지하고 호출자가 다시 활성화되어 프로그램 실행을 재개할 때까지 기다립니다. 복구 프로그램에서 사용하는 방법에 따라 Yield expr 표현식의 결과 값도 그에 따라 변경됩니다. __next()__로 호출하면 산출 표현식 결과 값은 None입니다. send()로 호출하면 산출 표현식 결과 값은 send 함수를 통해 전달된 값입니다. 다음은 공식 문서[1]에 소개된 Yield 표현식의 예로서, Yield 키워드의 특징과 사용법을 잘 설명할 수 있습니다.

>>> 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!!!

위 코드에 대한 설명은 다음과 같습니다.

Python에서 Yield 키워드의 기능은 무엇입니까?

  • 첫 번째 next(generator)가 실행되면, 즉 생성기가 사전 활성화됩니다. 생성기는 실행을 시작하고 실행이 value = (yield value) 위치에 도달하면 Yield 값이 먼저 출력됩니다. 숫자 1을 생성하기 위해 호출된 다음 생성기가 양보에서 일시 중지됩니다.

  • 두 번째 next(generator)가 호출되면 생성기는 실행을 재개합니다. next()를 사용하여 생성기 함수를 호출하므로 value의 값은 None이 되므로 생성기 함수는 항복 값까지 계속 실행됩니다. None 값을 인터프리터에 반환한 다음 다시 일시 중지합니다.

  • 그런 다음 send(2) 메서드를 사용하여 생성기를 계속 호출합니다. value는 들어오는 숫자 2를 수신하고 value = (yield value)를 계속 실행하고 숫자 2를 인터프리터에 반환한 다음 일시 중지합니다.

  • 이후 인터프리터는 throw(TypeError, "spam") 메서드를 통해 다시 호출하고 생성기는 실행을 재개하고 예외를 발생시킵니다. 생성기는 예외를 포착하고 예외 TypeError('spam')를 변수에 할당합니다. value , 그런 다음 value = (yield value)까지 프로그램이 다시 실행되고 TypeError('spam')이 인터프리터에 반환됩니다.

  • 마지막으로 프로그램은 close() 메서드를 호출하고 생성기 함수 위치에 GeneratorExit를 발생시키고 예외가 발생하며 생성기가 정상적으로 종료되고 마지막으로 가장 바깥쪽 try 문에 해당하는 finally 분기를 실행하고 인쇄합니다. 출력 정리.

파이썬에는 Generator라는 매우 유용한 구문이 있는데, 사용되는 키워드는 Yield입니다. 생성기 도구를 효과적으로 사용하면 시스템 리소스를 효과적으로 절약하고 불필요한 메모리 사용을 피할 수 있습니다.

Generator

놀랍지도 않게 처음으로 Yield를 접하게 될 때는 분명히 Generator 함수에 있을 것입니다. 제너레이터는 숫자나 다른 형태의 값을 연속적으로 생성하는 함수로, for 루프나 next() 함수를 통해 하나씩 호출할 수 있습니다. 여기서 강조해야 할 점은 생성기가 할당 없이 항복 표현식을 포함하므로 다음 두 형식은 동일합니다[2]:

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

여기에서 두 번째 형식을 강조하는 이유는 send() 메서드의 사용을 이해하기 위한 것입니다. 가치를 전달하려면 수익률을 더 잘 이해할 수 있습니다. 동시에, 제너레이터를 호출하여 반환되는 값은 항복 표현식 자체의 결과 값이 아니라, 항복 키워드 오른쪽에 있는 표현식 i + 1의 값이라는 것이 더 정확하게 설명될 수도 있습니다.

다음을 호출해 보겠습니다.

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

컨텍스트 관리자

Python의 contexlib 모듈에 있는 @contextmanager 데코레이터를 사용하면 Yield를 사용하여 컨텍스트 관리자를 정의할 수도 있습니다. 다음은 Python Tricks 책[3]의 예입니다.

from contextlib import contextmanager

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

위에서 데코레이터와 Yield 키워드를 통해 정의한 컨텍스트 관리자는 다음 클래스의 메서드 정의와 동일합니다.

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()

는 다음 메서드를 사용하여 별도로 호출할 수 있습니다.

>>> 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

Coroutine 개념이 가득 찼습니다. 아름다움을 완전히 이해하려면 여전히 약간의 노력이 필요합니다. 그러나 때로는 멀티스레딩이 코루틴보다 훨씬 더 많은 문제를 일으킬 수 있기 때문에 노력할 가치가 있습니다. 다음은 Python Cookbook[4]의 항복 표현식만 사용하여 작성된 코루틴의 예입니다. ​​

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 里非常特别的一个关键字,值得花时间好好掌握了解。

위 내용은 Python에서 Yield 키워드의 기능은 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.