この記事では主にPythonのyieldの使い方の簡単な分析を紹介していますが、編集者はそれが非常に優れていると考えたので、参考として共有します。エディターに従って、
フィボナッチ数列の生成方法を見てみましょう
フィボナッチ数列は非常に単純な再帰的の数値シーケンスです。最初と 2 番目の数値を除き、前の 2 つの数値を加算することで任意の数値を取得できます。数字。コンピューター プログラムを使用してフィボナッチ数列の最初の 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) を実行すると、次の出力が得られます:
>>> fab(5)
1
1
2
3
5
結果は問題ありませんが、経験豊富な開発者は、fab 関数で直接数値を出力するために print を使用すると、関数の再利用性が低下することを指摘します。これは、fab 関数が None を返し、他の関数が関数によって生成されたシーケンスを取得できないためです。
fab 関数の再利用性を向上させるには、シーケンスを直接出力せず、List を返すことが最善です。以下は、fab 関数の書き換えられた 2 番目のバージョンです:
List 2. フィボナッチ数列の最初の N 個の数値を出力します。 2 番目のバージョン
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 によって返された List を出力できます。 function:
>>> for n in fab(5): ... print n ... 1 1 2 3 5
書き換えられた fab 関数は List を返すことで再利用性の要件を満たすことができますが、より経験豊富な開発者は、メモリを制御したい場合、パラメータ max が増加するにつれて動作中にこの関数が占有するメモリが増加することを指摘するでしょう。使用法としては、中間結果を保存するために List を使用するのではなく、反復可能な オブジェクト を反復処理するのが最善です。たとえば、Python2 では、html" target="_blank">range
(1000): pass は 1000 個の要素のリストとなり、コードは次のようになります:
for i in xrange(1000) : pass
この場合、1000 個の要素のリストは生成されませんが、反復ごとに次の値が返され、占有されるメモリ領域は非常に少なくなります。 xrange は List を返すのではなく、反復可能なオブジェクトを返すためです。
class for i in <a href="http://www.php.cn/wiki/1078.html" target="_blank">range</a>(1000): pass
会导致生成一个 1000 个元素的 List,而代码:
for i in xrange(1000): pass
に書き直すことができます。 以下は Fab の 3 番目のバージョンです。
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 が便利です。
def fab(max): n, a, b = 0, 0, 1 while n < max: yield b # print b a, b = b, a + b n = n + 1 '''The 4 番目のバージョン 最初のバージョンと比較すると、 fab は print b を yield b に変更しただけで、単純さを維持しながら反復可能な効果を実現しました。
fab の 4 番目のバージョンの呼び出しは、fab の 2 番目のバージョンとまったく同じです。
>>> for n in Fab(5): ... print n ... 1 1 2 3 5
が実行されると、各ループは fab 関数内のコードを実行します。実行が yield b に達すると、fab 関数は反復値を返します。次の反復では、コードは yield の次のステートメントから実行を続けます。 b, そして、関数のローカル
変数
また、fab(5) の next() メソッドを手動で呼び出すこともできます (fab(5) は next() メソッドを持つジェネレーター オブジェクトであるため)。これにより、fab の実行プロセスをより明確に確認できます。 :
>>> 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 'f1:', f1.next() f1: 1 >>> print 'f2:', f2.next() f2: 1 >>> print 'f1:', f1.next() f1: 1 >>> print 'f2:', f2.next() f2: 1 >>> print 'f1:', f1.next() f1: 2 >>> print 'f2:', f2.next() f2: 2 >>> print 'f2:', f2.next() f2: 3 >>> print 'f2:', 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, 'rb') as f: while True: block = f.read(BLOCK_SIZE) if block: yield block else: return
以上がPython での yield の使用の概要の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。