Heim  >  Artikel  >  Backend-Entwicklung  >  Ausführliche Erklärung des Python-Generators in einem Artikel

Ausführliche Erklärung des Python-Generators in einem Artikel

WBOY
WBOYnach vorne
2022-06-09 16:02:172621Durchsuche

Dieser Artikel vermittelt Ihnen relevantes Wissen über Python, in dem hauptsächlich Probleme im Zusammenhang mit Generatoren vorgestellt werden, einschließlich des Konzepts von Generatoren, des Ausführungsprozesses von Generatoren, Ertrags- und Generatormethoden usw., wie folgt. Schauen wir uns das an, ich hoffe es wird für alle hilfreich sein.

Ausführliche Erklärung des Python-Generators in einem Artikel

Empfohlenes Lernen: Python-Video-Tutorial

Dieser Artikel vermittelt Ihnen relevantes Wissen über Python und stellt hauptsächlich verwandte Themen zu Generatoren vor, einschließlich des Konzepts von Generatoren und der Funktionen von Generatoren. Werfen wir einen Blick auf den Ausführungsprozess , Ertrags- und Generatormethoden usw. Ich hoffe, es wird für alle hilfreich sein.

1. Generator-Konzept

Generator (englisch: Generator) ist eine sehr faszinierende Sache und wird oft als fortgeschrittene Programmierfähigkeit in Python angesehen. Ich diskutiere dieses Thema jedoch gerne mit den Lesern hier – auch wenn Sie vielleicht ein Anfänger sind – denn ich glaube, dass der Zweck des Lesens dieses Tutorials nicht darin besteht, sich auf das Anfängerniveau zu beschränken. Sie müssen ein widerspenstiges Herz haben Werden Sie ein Python-Meister. Beginnen wir also damit, etwas über Generatoren zu lernen.

Erinnern Sie sich an den „Iterator“ im vorherigen Abschnitt? Generatoren und Iteratoren haben eine bestimmte Ursprungsbeziehung. Der Generator muss zwar iterierbar sein, aber darüber hinaus hat er nicht viele andere Verwendungsmöglichkeiten, sodass wir ihn als einen sehr praktischen benutzerdefinierten Iterator verstehen können.

2. Einfacher Generator

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

Ist das dem Listenverständnis sehr ähnlich? Beachten Sie genau, dass es sich nicht um eine Liste handelt, sondern um eine Liste:

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

Der Unterschied zwischen den beiden oben genannten ist [] oder (), aber die Ergebnisse sind völlig unterschiedlich.

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

Zur einfacheren Beobachtung habe ich die obigen Ergebnisse neu geordnet. Haben Sie die notwendigen Methoden __inter__() und next() in Iteratoren entdeckt, was zeigt, dass es sich um einen Iterator handelt? Wenn es sich um einen Iterator handelt, können Sie eine for-Schleife verwenden, um seine Werte nacheinander auszulesen

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

Wenn Sie ihn zum ersten Mal durchlaufen, lesen Sie die Werte in my_generator nacheinander aus und drucken sie aus. Wenn Sie es erneut lesen, stellen Sie fest, dass es keine Ergebnisse gibt. Über diese Funktion verfügen auch Iteratoren.

Bei dieser Liste ist es anders:

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

Könnte es sein, dass der Generator beim Listenparsen einfach [] durch () ersetzt? Dies ist lediglich eine Form des Ausdrucks und der Verwendung von Generatoren. In Anlehnung an die Benennung von

Listenverständnissen kann es als „generatoranalytischer Ausdruck“ (oder: Generatorableitung, Generatorausdruck) bezeichnet werden.

Generator-Parsing-Ausdrücke haben viele Verwendungsmöglichkeiten und sind an vielen Stellen eine gute Wahl, um Listen zu ersetzen. Insbesondere beim Umgang mit einer großen Anzahl von Werten belegen Listen, wie im vorherigen Abschnitt erwähnt, mehr Speicher. Der Vorteil von Iteratoren (Generatoren sind Iteratoren) besteht darin, dass sie weniger Speicher belegen, sodass keine Instanziierung des Generators (oder Iterators) erforderlich ist ) Verwandeln Sie es in eine Liste und bedienen Sie es direkt, um die Vorteile der Iteration zu zeigen. Zum Beispiel:

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

Achten Sie auf die obige sum()-Operation. Denken Sie nicht, dass eine Klammer fehlt, sie ist einfach so geschrieben. Ist es nicht bezaubernd? Wenn die Liste, müssen Sie:

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

Der durch die Generatoranalyse erhaltene Generator enthält einige Details des Generators, und die anwendbaren Bereiche sind begrenzt. Als Nächstes werden wir das Innere des Generators analysieren und ein tieferes Verständnis dieses magischen Werkzeugs erlangen.

3. Definitions- und Ausführungsprozess

Yield bedeutet auf Chinesisch „Produktion, Produktion“ und wird als Schlüsselwort verwendet (Sie können es nicht in den Namen von Variablen, Funktionen und Klassen verwenden). Symbol des Generators.

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

hat eine sehr einfache Funktion erstellt. Der einzige Unterschied zu den zuvor gezeigten Funktionen besteht in der Verwendung von drei Yield-Anweisungen. Führen Sie dann die folgenden Operationen aus:

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

Der Rückgabewert der oben festgelegten Funktion ist ein Objekt vom Typ Generator.

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

Ich habe hier __iter__() und next() gesehen, was darauf hinweist, dass es sich um einen Iterator handelt. In diesem Fall natürlich:

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

Anhand dieses einfachen Beispiels können wir erkennen, dass der Rückgabewert der Funktion, die das Schlüsselwort yield enthält, ein Objekt vom Typ Generator ist und dieses Generatorobjekt ein Iterator ist.

Wir nennen Funktionen, die Ertragsaussagengeneratoren enthalten. Ein Generator ist ein Iterator, der mit gewöhnlicher Funktionssyntax definiert ist. Wie aus dem obigen Beispiel ersichtlich ist, werden __inter__() und next() während des Definitionsprozesses dieses Generators (ebenfalls ein Iterator) nicht wie der Iterator im vorherigen Abschnitt geschrieben, solange die yield-Anweisung verwendet wird , die gewöhnliche Funktion Es wird auf magische Weise zu einem Generator und hat die funktionalen Eigenschaften eines Iterators. Die Funktion der

yield-Anweisung besteht darin, beim Aufruf den entsprechenden Wert zurückzugeben. Lassen Sie uns den oben genannten laufenden Prozess im Detail analysieren:

1. Außer der Rückgabe des Generators gibt es keine Operation und es wird kein Wert zurückgegeben.

2. ge.next(): Erst zu diesem Zeitpunkt beginnt der Generator mit der Ausführung. Wenn er auf die erste yield-Anweisung trifft, gibt er den Wert zurück und unterbricht die Ausführung (manche nennen es

).

为挂起)。

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视频教程

Das obige ist der detaillierte Inhalt vonAusführliche Erklärung des Python-Generators in einem Artikel. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:csdn.net. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen