ホームページ  >  記事  >  バックエンド開発  >  Pythonジェネレーターを1つの記事で詳しく解説

Pythonジェネレーターを1つの記事で詳しく解説

WBOY
WBOY転載
2022-06-09 16:02:172646ブラウズ

この記事では、python に関する関連知識を提供します。主に、ジェネレーターの概念、ジェネレーターの実行プロセス、収量と生成コンテナー メソッドなどの内容を含む、ジェネレーターに関連する問題を紹介します。ぜひ参考にしてみてください。

Pythonジェネレーターを1つの記事で詳しく解説

推奨学習: Python ビデオ チュートリアル

この記事では、主要な概要を含む Python に関する関連知識を提供します。ジェネレーターの概念、ジェネレーターの実行プロセス、利回りとジェネレーターのメソッドなど、ジェネレーターに関する問題を一緒に見ていきましょう。

1. ジェネレーターの概念

ジェネレーター (英語: ジェネレーター) は非常に魅力的なものであり、Python の高度なプログラミング スキルであると考えられています。ただし、たとえあなたが初心者であっても、

喜んでこのトピックについて読者と議論できます。なぜなら、このチュートリアルを読む目的は単に初心者レベルに限定することではないと私は信じているからです。手に負えない心を持っている - Pythonマスターになること。それでは、ジェネレーターについて学び始めましょう。

前のセクションの「反復子」を覚えていますか?ジェネレータとイテレータには特定の起源関係があります。確かに単なるイテレータではありませんが、それ以外にあまり用途がないので、非常に便利な自動 Define イテレータとして理解できます。

2. シンプルなジェネレーター

>>> my_generator = (x*x for x in range(4))

これはリスト分析に非常に似ていますか?よく見てください、これはリストではありません このように取得できれば、それはリストです:

>>> my_list = [x*x for x in range(4)]

上の 2 つの違いは [] または () です。わずかな違いですが、結果はは全く異なります。

>>> dir(my_generator)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__',
'__iter__',
'__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running',
'next',
'send', 'throw']

観察しやすいように、上記の結果を再整理しました。イテレータに必要なメソッド __inter__() と next() を発見しましたか。これは、それがイテレータであることを示しています。イテレータの場合、for ループを使用してその値を順番に読み出すことができます。

>>> for i in my_generator:
... print i
...
0
1
4
9
>>> for i in my_generator:
... print i
...

初めてループされると、my_generator の値が読み出されて出力されます順番に読み取られますが、再度読み取られても結果が見つかりませんでした。この機能はイテレータにも備わっています。

そのリストの場合は異なります:

>>> for i in my_list:
... print i
...
0
1
4
9
>>> for i in my_list:
... print i
...
0
1
4
9

ジェネレーターはリスト解析中の [] を () に置き換えるだけですか?これは、ジェネレーターの表現と使用法の一形式にすぎません。

リスト分析式の命名に従って、「ジェネレーター分析式」(または、ジェネレーター導出、ジェネレーター式) と呼ぶことができます。

ジェネレーター解析式には多くの用途があり、さまざまな場所でリストを置き換えるのに適しています。特に、前のセクションで説明したように、多数の値を扱う場合、リストはより多くのメモリを占有します。イテレータ (ジェネレータはイテレータです) の利点は、占有メモリが少ないことです。そのため、ジェネレータ (またはイテレータ) をインスタンス化する必要がありません。 ) それをリストに変換し、直接操作して反復の利点を示します。例:

>>> sum(i*i for i in range(10))
285

上記の sum() 操作に注意してください。括弧が抜けているとは思わないでください。単にこのように記述されているだけです。魅力的ではないですか?

>>> sum([i*i for i in range(10)])
285

ジェネレーター解析で得られたジェネレーターは、ジェネレーターの詳細が一部隠蔽されており、適用できる範囲が限られています。次に、ジェネレーターの内部を分析し、この魔法のツールについて理解を深めます。

3. 定義と実行プロセス

yield という単語は中国語で「生産、生産」を意味し、Python ではキーワードとして使用されます (変数、関数、クラスで使用します

は名前に使用できません)。これはジェネレーターのシンボルです。

>>> def g():
... yield 0
... yield 1
... yield 2
...
>>> g
<function g at 0xb71f3b8c>

非常に単純な関数を作成しました。これまでに見た関数との唯一の違いは、3 つの yield ステートメントを使用していることです。次に、次の操作を実行します。

>>> ge = g()
>>> ge
<generator object g at 0xb7200edc>
>>> type(ge)
<type &#39;generator&#39;>

上で作成した関数の戻り値は、ジェネレーター型のオブジェクトです。

>>> dir(ge)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__iter__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running', 'next', 'send', 'throw']

ここに __iter__() と next() があり、それがイテレータであることを示しています。この場合、もちろん次のようになります。

>>> ge.next()
0
>>> ge.next()
1
>>> ge.next()
2
>>> ge.next()
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
StopIteration

この簡単な例からわかるように、yield キーワードを含む関数の戻り値はジェネレータ型のオブジェクトであり、このジェネレータ オブジェクトはイテレータです。

yield ステートメントを含む関数をジェネレーターと呼びます。ジェネレーターは、通常の関数構文で定義されたイテレーターです。上の例からわかるように、このジェネレータ (イテレータでもあります) の定義プロセスでは、__inter__() と next() は前のセクションのイテレータのようには記述されません。代わりに、yield ステートメントが使用されている限り、 、通常の関数 これは魔法のようにジェネレーターになり、イテレーターの機能特性を持ちます。

yield ステートメントの機能は、呼び出されたときに対応する値を返すことです。上記の実行プロセスを詳細に分析します:

1. ge = g(): ジェネレーターを返す以外、操作はなく、値も返されません。

2. ge.next(): ジェネレーターが実行を開始するのはこの時点までではありません。最初の yield ステートメントに遭遇すると、値を返し、実行を一時停止します (これを

と呼ぶ人もいます)

为挂起)。

3. ge.next() :从上次暂停的位置开始,继续向下执行,遇到 yield 语句,将值返回,又暂停。

4. gen.next() :重复上面的操作。

5. gene.next() :从上面的挂起位置开始,但是后面没有可执行的了,于是 next() 发出异常。

从上面的执行过程中,发现 yield 除了作为生成器的标志之外,还有一个功能就是返回值。那么它跟 return 这个返回值有什么区别呢?

4. yield

为了弄清楚 yield 和 return 的区别,我写了两个函数来掩饰:

>>> def r_return(n):
... print "You taked me."
... while n > 0:
... print "before return"
... return n
... n -= 1
... print "after return"
...
>>> rr = r_return(3)
You taked me.
before return
>>> rr
3

从函数被调用的过程可以清晰看出, rr = r_return(3) ,函数体内的语句就开始执行了,遇到 return,将值返

回,然后就结束函数体内的执行。所以 return 后面的语句根本没有执行。这是 return 的特点

下面将 return 改为 yield:

>>> def y_yield(n):
... print "You taked me."
... while n > 0:
...     print "before yield"
...     yield n
...     n -= 1
...     print "after yield"
...
>>> yy = y_yield(3) #没有执行函数体内语句
>>> yy.next() #开始执行
You taked me.
before yield
3 #遇到 yield,返回值,并暂停
>>> yy.next() #从上次暂停位置开始继续执行
after yield
before yield
2 #又遇到 yield,返回值,并暂停
>>> yy.next() #重复上述过程
after yield
before yield
1
>>> yy.next()
after yield #没有满足条件的值,抛出异常
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
StopIteration

结合注释和前面对执行过程的分析,读者一定能理解 yield 的特点了,也深知与 return 的区别了。

一般的函数,都是止于 return。作为生成器的函数,由于有了 yield,则会遇到它挂起,如果还有 return,遇到它就直接抛出 SoptIteration 异常而中止迭代。

#!/usr/bin/env Python
# coding=utf-8

def fibs(max):
    """
    斐波那契数列的生成器
    """
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1
if __name__ == "__main__":
    f = fibs(10)
    for i in f:
        print i ,

运行结果如下:

$ python 21501.py
1 1 2 3 5 8 13 21 34 55

用生成器方式实现的斐波那契数列是不是跟以前的有所不同了呢?大家可以将本教程中已经演示过的斐波那契数列实现方式做一下对比,体会各种方法的差异。

经过上面的各种例子,已经明确,一个函数中,只要包含了 yield 语句,它就是生成器,也是迭代器。这种方式显然比前面写迭代器的类要简便多了。但,并不意味着上节的就被抛弃。是生成器还是迭代器,都是根据具体的使用情景而定。

5. 生成器方法

在 python2.5 以后,生成器有了一个新特征,就是在开始运行后能够为生成器提供新的值。这就好似生成器

和“外界”之间进行数据交流。

>>> def repeater(n):
... while True:
...     n = (yield n)
...
>>> r = repeater(4)
>>> r.next()
4
>>> r.send("hello")
'hello

当执行到 r.next() 的时候,生成器开始执行,在内部遇到了 yield n 挂起。注意在生成器函数中, n = (yield

n) 中的 yield n 是一个表达式,并将结果赋值给 n,虽然不严格要求它必须用圆括号包裹,但是一般情况都这

么做,请大家也追随这个习惯。

当执行 r.send("hello") 的时候,原来已经被挂起的生成器(函数)又被唤醒,开始执行 n = (yield n) ,也就是

讲 send() 方法发送的值返回。这就是在运行后能够为生成器提供值的含义。

如果接下来再执行 r.next() 会怎样?

>>> r.next()

什么也没有,其实就是返回了 None。按照前面的叙述,读者可以看到,这次执行 r.next() ,由于没有传入任何值,yield 返回的就只能是 None.

还要注意,send() 方法必须在生成器运行后并挂起才能使用,也就是 yield 至少被执行一次。如果不是这样:

>>> s = repeater(5)
>>> s.send("how")
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
TypeError: can't send non-None value to a just-started generator

就报错了。但是,可将参数设为 None:

>>> s.send(None)
5

这是返回的是调用函数的时传入的值。

此外,还有两个方法:close() 和 throw()

• throw(type, value=None, traceback=None):用于在生成器内部(生成器的当前挂起处,或未启动时在定

义处)抛出一个异常(在 yield 表达式中)。

• close():调用时不用参数,用于关闭生成器。

推荐学习:python视频教程

以上がPythonジェネレーターを1つの記事で詳しく解説の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はcsdn.netで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。