1. 程式人生 > >Python 生成器的next和send

Python 生成器的next和send

1.什麼是生成器? 在 Python 中,使用了 yield 的函式被稱為生成器(generator)。 跟普通函式不同的是,生成器是一個返回迭代器的函式,只能用於迭代操作,更簡單點理解生成器就是一個可以迭代的東西。

2.next()與send() next()方法: 在呼叫生成器執行的過程中,每次遇到 yield ,函式返回當前的值,並且會暫停並儲存當前所有的執行資訊, 並在下一次執行 next() 方法時從當前位置繼續執行。

例項:

def _generator():
    r = "Here"
    for i in range(3):
        yield r
        r = '200 OK'
+ str(i) g = _generator() # 生成器物件 first_next = g.__next__() # 第一次next second_next = g.__next__() # 第二次next third_next = g.__next__() # 第三次next print(first_next) print(second_next) print(third_next)

執行結果:

Here
200 OK0
200 OK1

程式碼解讀:

首先,定義的 g=_generator() 並不是函式呼叫,而是產生生成器物件。

第一次,我們呼叫next
()方法,即:first_next = g.__next__(), 此時我們進入生成器函式_generator(),遇到yield返回,此時,r="Here", 所以,可以看到結果第一行為:"Here"。 第二次,我們同樣呼叫了next()方法,即:second_next = g.__next__(), 此時我們也是進入生成器,但是並不是從頭開始進入執行,而是從上一次的yield後面開始, 即:執行 r = '200 OK' + str(i),此時 r = '200 OK0',遇到yield返回, 所以,可以看到結果第一行為:"200 OK0"。 第三次,我們依舊呼叫了next()方法,即:third_next = g.__next__
(), 此時我們也是進入生成器,但是並不是從頭開始進入執行,而是從上一次的yield後面開始, 即:執行 r = '200 OK' + str(i),此時 r = '200 OK1',遇到yield返回, 所以,可以看到結果第一行為:"200 OK1"。 如果我們再加多有一次呼叫,forth_next = g.__next__() # 第四次next, 就會報錯: Traceback (most recent call last): File "C:/Users/10475/Desktop/Spider_Project/Scrapy_Project/tools/multiprocessing_demo.py", line 76, in <module> forth_next = g.__next__() # 第四次next StopIteration 原因是: 執行3yield後,此時,i已經為2了,頂天了,已經沒有yield可以執行了, 所以,第4次呼叫next(o)就報錯。 在平時使用生成器,我們更多的去使用for迴圈去迭代生成器,上面也提到, generator是一個可以迭代的物件,所以結果也是一樣的。

例項改寫:

def _generator():
    r = "Here"
    for i in range(3):
        yield r
        r = '200 OK' + str(i)

g = _generator()    # 生成器物件
for i in g:
    print(i)

send()方法: 先理解個概念【掛起】:意思就是暫時保留先不進行,等待需要時再進行。 作用:與next()作用相似 區別: 1.send(value)可以傳遞value給yield,即:我們可以指定yield返回啥就返回啥, 2.next()不能傳遞特定的值,只能傳遞None進去。

第一次呼叫時,請使用next()語句或是send(None),不能使用send傳送一個非None的值,否則會出錯的,可以看到,can't send non-None value to a just-started generator因為生成器just-started generator,是沒有Python yield語句來接收這個值的。

Traceback (most recent call last):
  File "C:/Users/10475/Desktop/Spider_Project/Scrapy_Project/tools/multiprocessing_demo.py", line 87, in <module>
    g.send(1)
TypeError: can't send non-None value to a just-started generator

例項1:

def _generator():
    for i in range(4):
        n = yield i
        if n == 'hello':
            print('world')
        else:
            print(str(n))

g = _generator()     # 生成器物件
print(g.__next__())  # 結果是:0
print(g.__next__())  # 結果是:None

執行結果:

0
None
1

程式碼解讀:

第一次呼叫g.__next__(),跑到 yield i,遇到yield,返回結果為0。

第二次呼叫g.__next__(),繼續從上一次狀態繼續執行,此時,需要注意,
我們執行的起點是:n = yield i,並且這個n值並不是i值,而是通過send()傳遞過來的值,
即:n = send(),但是我們沒有呼叫send()方法,所以,n自然而然為None,
所以,此時是執行print(g.__next__()),結果為None。

緊接著,我們還在繼續,yield完以後,n就有值了,此時為None,
我們進入判斷語句,此時是執行print(str(n)),結果為1

例項2:

def _generator():
    for i in range(4):
        n = yield i
        if n == 'hello':
            print('world')
        else:
            print(str(n))

g = _generator()     # 生成器物件
# print(g.__next__())
print(g.send(None))  # 相當於g.__next()__

程式碼解讀:

呼叫send()方法,傳入value為None,此時相當於next()方法,效果自然與next()一樣,
即:遇到yield就返回,返回結果為:0,所以print(0)

例項3:

def _generator():
    for i in range(4):
        n = yield i
        if n == 'hello':
            print('world')
        else:
            print(str(n))

g = _generator()     # 生成器物件
g.send(None)         # 相當於g.__next__()
g.send('100')
g.send('Python')
g.send('hello')

執行結果:

100
Python
world

程式碼解讀:

g.send(None):
相當於g.__next__()作用,遇到yield返回,返回一個0,由於沒有誰列印0,自然看不到。

g.send('100'):
繼續從上一次狀態出發,起點為:n ,(再次提醒:n 不等於 yield i)此時n = send(100),
即:n = 100,進入判斷,print(str(n)),結果也就是我們看到的第一行100。

g.send('Python'):
繼續上一次狀態出發,起點為:for i in range(4),
此時n = send('Python'),即:n = 'Python',進入判斷,print(str(n)),
結果也就是我們看到的第二行"Python"。

g.send('hello'):
繼續上一次狀態出發,起點為:for i in range(4),
此時 n = send('hello'),即:n = 'hello',進入判斷,print('world'),
結果也就是我們看到的第三行"world"。

如果我們再加多一行:g.send('Scrapy'),結果為:
Traceback (most recent call last):
100
  File "C:/Users/10475/Desktop/Spider_Project/Scrapy_Project/tools/multiprocessing_demo.py", line 91, in <module>
Python
world
Scrapy
    g.send('Scrapy')
StopIteration

為什麼會報錯?
因為超出了range(4),前面已經實現了4次,(send(None)也要算上),
這一次加了g.send('Scrapy')是第五次,所以報錯。

為什麼還會有輸出?
我的理解是:雖然報錯,但是,我壓根都不需要你這個i,我的n = send('Scrapy'),
即:n = "Scrapy",依舊進入迴圈,依舊print(str(n)),所以有輸出。