>  기사  >  백엔드 개발  >  Python의 Yield 사용 소개

Python의 Yield 사용 소개

黄舟
黄舟원래의
2017-05-21 13:55:471377검색

이 글은 주로 Python의 사용법에 대한 간략한 분석을 소개합니다. 편집기를 따라 피보나치 수열을 생성하는 방법을 살펴보겠습니다


피보나치 수열은 매우 간단합니다재귀적 수열입니다. 첫 번째와 두 번째 숫자의 경우 처음 두 숫자를 더하면 어떤 숫자라도 얻을 수 있습니다. 컴퓨터 프로그램을 사용하여 피보나치 수열의 첫 번째 N개 숫자를 출력하는 것은 매우 간단한 문제입니다. 많은 초보자는 다음 함수를 쉽게 작성할 수 있습니다.

목록 1. 피보나치 수열의 첫 번째 N개 숫자에 대한 간단한 출력

 def fab(max): 
  n, a, b = 0, 0, 1 
  while n < max: 
    print b 
    a, b = b, a + b 
    n = n + 1

fab(5)를 실행하면 다음 출력을 얻을 수 있습니다.

>>> ; (5)
1
1
2
3
5

결과는 문제없으나 숙련된 개발자가 Fab에서 직접 지적할 것입니다. 함수에서 숫자를 인쇄하면 함수의 재사용성이 떨어지게 됩니다. 왜냐하면 fab 함수는 None을 반환하고 다른 함수는 함수에 의해 생성된 시퀀스를 얻을 수 없기 때문입니다.

fab 기능의 재사용성을 높이려면 시퀀스를 직접 인쇄하는 것이 아니라 목록을 반환하는 것이 가장 좋습니다. 다음은 fab 함수의 다시 작성된 두 번째 버전입니다.

목록 2. 피보나치 수열의 첫 번째 N 숫자 출력 두 번째 버전

 def fab(max): 
  n, a, b = 0, 0, 1 
  L = [] 
  while n < max: 
    L.append(b) 
    a, b = b, a + b 
    n = n + 1 
  return L

다음을 수행할 수 있습니다. fab 함수에서 반환된 목록을 인쇄하려면 다음 방법을 사용하세요.

>>> 
for
 n in fab(5): 
 ...     print n 
 ... 
 1 
 1 
 2 
 3 
 5

다시 작성된 fab 함수는 목록을 반환하여 재사용성 요구 사항을 충족할 수 있지만 경험이 많은 개발자는 이 함수가 작업 중 차지하는 메모리는 매개변수 최대값이 증가함에 따라 증가합니다. 메모리 점유를 제어하려면 List를 사용하여 중간 결과를 저장하는 것이 아니라 반복 가능한 객체 를 통해 반복하는 것이 가장 좋습니다. 예를 들어 Python2에서 http://www.php.cn/wiki/1078.html" target="_blank">range
(1000): pass는 1000개 요소의 목록을 생성합니다. 코드

는 1000개 요소의 목록을 생성하지 않지만 각 반복에서 다음 값을 반환하므로 메모리 공간을 거의 차지하지 않습니다. xrange는 List가 아니라 반복 가능한 객체를 반환하기 때문입니다.

iterable을 사용하면 fab 함수를 iterable을 지원하는  for i in <a href="http://www.php.cn/wiki/1078.html" target="_blank">range</a>(1000): pass클래스

로 다시 작성할 수 있습니다. 다음은 Fab의 세 번째 버전입니다.

 for i in xrange(1000): pass

목록 4. 세 번째 버전

class Fab(object): 

  def init(self, max): 
    self.max = max 
    self.n, self.a, self.b = 0, 0, 1 

  def iter(self): 
    return self 

  def next(self): 
    if self.n < self.max: 
      r = self.b 
      self.a, self.b = self.b, self.a + self.b 
      self.n = self.n + 1 
      return r 
    raise StopIteration()
Fab 클래스는 next()를 통해 시퀀스의 다음 숫자를 연속적으로 반환하고 메모리 사용량은 항상 일정합니다.

 >>> for n in Fab(5): 
 ...     print n 
 ... 
 1 
 1 
 2 
 3 
 5

그러나 이 버전은 클래스를 사용하여 다시 작성되었으므로 코드는 fab 함수의 첫 번째 버전보다 훨씬 덜 간결합니다. 반복 가능한 효과를 계속 얻으면서 fab 함수의 첫 번째 버전의 단순성을 유지하려는 경우 Yield가 유용합니다.

목록 5. Yield를 사용하는 네 번째 버전

 def fab(max): 
  n, a, b = 0, 0, 1 
  while n < max: 
    yield b 
    # print b 
    a, b = b, a + b 
    n = n + 1 

&#39;&#39;&#39;

첫 번째 버전과 비교했을 때, 네 번째 버전의 Fab에서는 print b만 항복 b로 변경하여 단순성을 유지하면서 반복 가능한 효과를 얻습니다.

fab의 네 번째 버전을 호출하는 것은 fab의 두 번째 버전과 완전히 동일합니다.

 >>> for n in Fab(5): 
 ...     print n 
 ... 
 1 
 1 
 2 
 3 
 5

간단히 말하면, Yield의 기능은 기능을 바꾸는 것입니다. Yield가 있는 함수는 더 이상 일반 함수가 아닙니다. Python 인터프리터는 이를 생성기로 처리합니다. fab(5)를 호출하면 fab 함수가 실행되지 않지만 반복 가능한 객체가 반환됩니다. for

루프
가 실행되면 각 루프는 fab 함수 내부의 코드를 실행합니다. 항복 b에 도달하면 fab 함수는 다음 반복에서 반복 값을 반환합니다. b. 실행이 계속되고 함수의 로컬

변수

가 마지막 중단 이전과 완전히 동일해 보이므로 함수는 다시 Yield를 만날 때까지 계속 실행됩니다.

fab(5)의 next() 메서드를 수동으로 호출할 수도 있습니다(fab(5)는 next() 메서드가 있는 생성기 개체이기 때문). 명확하게 fab에 대한 실행 프로세스: 목록 6. 실행 프로세스

 >>> f = fab(5) 
 >>> f.next() 
 1 
 >>> f.next() 
 1 
 >>> f.next() 
 2 
 >>> f.next() 
 3 
 >>> f.next() 
 5 
 >>> f.next() 
 Traceback (most recent call last): 
 File "<stdin>", line 1, in <module> 
 StopIteration

함수 실행이 끝나면 생성기가 자동으로 StopIteration 예외를 발생시킵니다. 반복이 완료되었습니다. for 루프에서는 StopIteration 예외를 처리할 필요가 없으며 루프는 정상적으로 종료됩니다.

다음과 같은 결론을 내릴 수 있습니다.

一个带有 yield 的函数就是一个 generator,它和普通函数不同,生成一个 generator 看起来像函数调用,但不会执行任何函数代码,直到对其调用 next()(在 for 循环中会自动调用 next())才开始执行。虽然执行流程仍按函数的流程执行,但每执行到一个 yield 语句就会中断,并返回一个迭代值,下次执行时从 yield 的下一个语句继续执行。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。

yield 的好处是显而易见的,把一个函数改写为一个 generator 就获得了迭代能力,比起用类的实例保存状态来计算下一个 next() 的值,不仅代码简洁,而且执行流程异常清晰。

如何判断一个函数是否是一个特殊的 generator 函数?可以利用 isgeneratorfunction 判断:

清单 7. 使用 isgeneratorfunction 判断

 >>> from inspect import isgeneratorfunction 
 >>> isgeneratorfunction(fab) 
 True

要注意区分 fab 和 fab(5),fab 是一个 generator function,而 fab(5) 是调用 fab 返回的一个 generator,好比类的定义和类的实例的区别:

清单 8. 类的定义和类的实例

 >>> import types 
 >>> isinstance(fab, types.GeneratorType) 
 False 
 >>> isinstance(fab(5), types.GeneratorType) 
 True
fab 是无法迭代的,而 fab(5) 是可迭代的:
 >>> from collections import Iterable 
 >>> isinstance(fab, Iterable) 
 False 
 >>> isinstance(fab(5), Iterable) 
 True

每次调用 fab 函数都会生成一个新的 generator 实例,各实例互不影响:

>>> f1 = fab(3) 
 >>> f2 = fab(5) 
 >>> print &#39;f1:&#39;, f1.next() 
 f1: 1 
 >>> print &#39;f2:&#39;, f2.next() 
 f2: 1 
 >>> print &#39;f1:&#39;, f1.next() 
 f1: 1 
 >>> print &#39;f2:&#39;, f2.next() 
 f2: 1 
 >>> print &#39;f1:&#39;, f1.next() 
 f1: 2 
 >>> print &#39;f2:&#39;, f2.next() 
 f2: 2 
 >>> print &#39;f2:&#39;, f2.next() 
 f2: 3 
 >>> print &#39;f2:&#39;, f2.next() 
 f2: 5

return 的作用

在一个 generator function 中,如果没有 return,则默认执行至函数完毕,如果在执行过程中 return,则直接抛出 StopIteration 终止迭代。

另一个例子

另一个 yield 的例子来源于文件读取。如果直接对文件对象调用 read() 方法,会导致不可预测的内存占用。好的方法是利用固定长度的缓冲区来不断读取文件内容。通过 yield,我们不再需要编写读文件的迭代类,就可以轻松实现文件读取:

清单 9. 另一个 yield 的例子

 def read_file(fpath): 
  BLOCK_SIZE = 1024 
  with open(fpath, &#39;rb&#39;) as f: 
    while True: 
      block = f.read(BLOCK_SIZE) 
      if block: 
        yield block 
      else: 
        return

위 내용은 Python의 Yield 사용 소개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

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