一些關於python生成器的「坑」

2021-08-18 18:02:06 字數 3400 閱讀 3304

首先,先看**:

def

add(s, x):

return s + x

defgen

():for i in range(4):

yield i

base = gen()

for n in [1, 10]:

base = (add(i, n) for i in base)

print list(base)

輸出的結果是【20,21,22,23】,也許你會有其他答案

那麼怎麼得出來的呢?

說到生成器,你必須要知道迭代器(iterator),它只能迭代一次,且遵從迭代器協議:

1.定義了__iter__方法,但是必須返回自身

2.定義了next方法,在python3中是__next__,用來返回下乙個值,當沒有資料可返回時,會丟擲乙個stopiteration錯誤

3.可以保持當前的狀態

樣例

自定義iterator 如何讓自定義的類的物件成為迭代器物件,其實就是定義__iter__next方法:

class

dataiter

(object):

def__init__

(self, *args):

self.data = list(args)

self.ind = 0

def__iter__

(self):

#返回自身

return self

defnext

(self):

# 返回資料

if self.ind == len(self.data):

raise stopiteration

else:

data = self.data[self.ind]

self.ind += 1

return data

那麼為什麼可以使用for來迭代迭代器物件呢,原因就是for替我們做了next的活,以及接收stopiteration的處理。

迭代器大概如此,下面介紹主角:優雅的迭代器——生成器(generator)

首先,因為生成器遵循了迭代器協議,所以生成器也是一種迭代器

建立生成器的方式有兩種:

1.yield

生成器函式跟普通函式只有一點不一樣,就是把 return 換成yield,其中yield是乙個語法糖,內部實現了迭代器協議,同時保持狀態可以掛起。如下:

記住一點,yield是資料的生產者,而諸如for等是資料的消費者。

def

gen():

print

'begin: generator'

i = 0

while

true:

print

'before return ', i

yield i

i += 1

print

'after return ', i

a = gen()

a.next()

首先,看到while true時不比驚慌,它只會乙個乙個被動的執行,不會主動去執行。當呼叫gen()時,並沒有真實執行函式,而是只是返回了乙個生成器物件。執行第一次a.next()時,才真正執行函式,執行到yield乙個返回值,然後就會掛起,保持當前的名字空間等狀態。然後等待下一次的呼叫,從yield的下一行繼續執行。說白了就是你需要資料(被檢索)的時候,才會執行。

2.生成器表示式

a = (i for i in range(10))
生成器表示式會建立乙個生成器,而且生成器有個特點就是惰性計算, 只有在被檢索時候,才會被賦值,正如上邊所說。

再來看乙個例子:

def

multipliers

():return (lambda x : i * x for i in range(4))

print [m(2) for m in multipliers()]

輸出結果為【0,2,4,6】,不清楚則可以看以下等價寫法:

def

multipliers

():for x in range(4):

defcd(i):

return x*i

yield cd

print [m(2) for m in multipliers()]

但是需要注意以下例子

def

multipliers

():return [lambda x : i * x for i in range(4)]

print [m(2) for m in multipliers()]

此例輸出為【6,6,6,6】而不是【0,2,4,6】,原因是因為閉包的延遲繫結。當迴圈結束時,i 的值已經是3, 此時結果都是6。不清楚可以看以下等價寫法:

def

multipliers

(): res =

for i in range(4):

definner

(j):

return j * i

return res

print [m(2) for m in multipliers()]

乙個解決的方法便是,使用預設引數繫結數值,如下:

def

multipliers

(): res =

for i in range(4):

definner

(j, i = i):

return j * i

return res

print [m(2) for m in multipliers()]

或者:

def

multipliers

():return [lambda x, i = i : i * x for i in range(4)]

print [m(2) for m in multipliers()]

參考頁:

python 中關於推導式生成器的一些總結

推導式 可以理解為是資料生成方式或者是處理方式 型別 列表,元組,字串,字典,集合 外部包裝的括號決定了返回值型別的 定義 表示式 for迴圈 if語句 1 對列表中的每項元素進行立方運算 變換功能 a 1,2,3,4,5,6,7,8,9,10 b x 3 for x in a print a pr...

Python練習 生成器 乙個生成器被坑的體無完膚

如下,盡可能獨立閱讀 1 2 from urllib.request import urlopen 匯入乙個包,這就是egon留的乙個坑 3def get url 這是為了保證題目的原裝性所以還是要有乙個url引數,實際上完全沒有這個必要 4def index 可能是egon想要我們更加熟悉閉包的概...

Python的生成器(一)

python的生成器是其有魅力的地方之一,它的好處是不用一開始就生成一大堆的資料,而是在需要的時候才計算出資料,我們首先先來看一下怎樣建立乙個生成器 建立生成器很簡單,我們來看第一種 x i for i in range 11 看一下是不是很簡單,看起來是不是很熟悉,怎麼像是列表生成式,是的,但是需...