Python——生成器、列表生成式、迭代器

Python列表生成式

列表推導(dǎo)式的一般語(yǔ)法

[express for item1 in iterable1 if condition1
for item2 in iterable2 if condition2
....
for itemn in iterablen if conditionn
]

這種語(yǔ)法等價(jià)于以下代碼

s = []
for item1 in iterable1:
      if condition1:
          for item2 in iterable2:
              if condition2:
                ....
                for itemN in iterableN:
                    if conditionN:
                        expression

下面舉一些列表推導(dǎo)式的栗子:

a = [1,3,5,7,9,10]
b = ['a','b','c','d','e']

# a列表中所有項(xiàng)*2
c = [2*s for s in a]
print(c)
# 帶有條件的列表推導(dǎo)式
d = [s for s in a if s>=5]
print(d)

# 多item
e = [(x,y) for x in a for y in b if x > 0]
print(e)

  • Python中生成列表可以使用如下的表達(dá)式:
a = [x for x in range(10)]
# 0,1,2,3,4,5,6,7,8,9
  • 元組或列表賦值
t = ('128',222)
a,b = t
這里a,b將被賦值為t的兩個(gè)元素
等價(jià)于 a = t[0] b =t[1]
print(a)
print(b)
# 128 222

生成器

通過(guò)列表生成式,我們可以直接創(chuàng)建一個(gè)列表,但是受到內(nèi)存的限制,列表的容量是有限的。而且我們創(chuàng)建的數(shù)據(jù)過(guò)于龐大會(huì)占用很大的內(nèi)存空間。所以,如果列表元素可以按照某種算法推算出來(lái),我們就可以在循環(huán)的過(guò)程中不斷推算后續(xù)的元素。這樣就不必創(chuàng)建列表。從而節(jié)省了大量的空間。在Python中,我們稱(chēng)這種一邊循環(huán)一邊計(jì)算的機(jī)制,稱(chēng)為生成器 generator,生成器用來(lái)創(chuàng)建Python序列的一個(gè)對(duì)象。它可以迭代龐大的序列,且不需要在內(nèi)存中創(chuàng)建和存儲(chǔ)整個(gè)序列。任何使用yield的函數(shù)都稱(chēng)為生成器

生成器的創(chuàng)建

通過(guò)列表生成式創(chuàng)建

要?jiǎng)?chuàng)建一個(gè)generator,我們可以把列表生成式的[]改成一個(gè)()這樣就創(chuàng)建了一個(gè)generator
生成器表達(dá)式是一個(gè)對(duì)象,它執(zhí)行的計(jì)算與列表推導(dǎo)相同,但會(huì)迭代地生成結(jié)果。它的語(yǔ)法也與列表推導(dǎo)式相同,但要用圓括號(hào)代替方括號(hào)

  • 生成器表達(dá)式的語(yǔ)法
(expression for item1 in iterable1 if condition1 
for item2 in iterable2 if condition2
...
for itemN in iterableN if conditionN
)

生成器表達(dá)式和列表推導(dǎo)式不同,生成器表達(dá)式實(shí)際上不創(chuàng)建列表或者立即對(duì)圓括號(hào)內(nèi)的表達(dá)式求值。相反,它會(huì)創(chuàng)建一個(gè)生成器對(duì)象,該對(duì)象通過(guò)迭代并按照需要生成的值,看個(gè)栗子

a = [1, 2, 3, 4]
b = (10 * i for i in a)
print(type(b))
print(b.__next__())
print(next(b))
>>>
<class 'generator'>
10
20

**使用生成器表達(dá)式和列表推導(dǎo)式可以極大地提高性能和n

L   = [x * x for x in range(10)]
L
g = (x*x for x in range(10))
g
# <generator object <genexpr> at 0x1022ef630>

這里L(fēng)是一個(gè)list g是一個(gè)generator

使用yield

生成器本質(zhì)上其實(shí)是一個(gè)函數(shù),函數(shù)就會(huì)有返回值,我們?cè)诤瘮?shù)中使用yield

def gerator():
    print('ok')
    yield 1
    print('next')
    yield 2

g = gerator()
print(g)
print(next(g))
print(next(g))
######
<generator object gerator at 0x102210fc0>
ok
1
next
2

如果一個(gè)函數(shù)定義中包含yield關(guān)鍵字,那么這個(gè)函數(shù)就不再是一個(gè)普通函數(shù),而是一個(gè)generator

generator和函數(shù)的執(zhí)行流程不一樣。函數(shù)是順序執(zhí)行,遇到return語(yǔ)句或者最后一行函數(shù)語(yǔ)句就返回。而變成generator的函數(shù),在每次調(diào)用next()的時(shí)候執(zhí)行,遇到y(tǒng)ield語(yǔ)句后返回。再次執(zhí)行時(shí)從上次返回的yield語(yǔ)句處繼續(xù)執(zhí)行

我們這樣定義函數(shù)后,直接執(zhí)行這個(gè)函數(shù)不會(huì)被內(nèi)部執(zhí)行了,這個(gè)函數(shù)就會(huì)成為一個(gè)生成器對(duì)象
上面的生成器函數(shù)中,我們執(zhí)行完第一個(gè)next中,程序會(huì)執(zhí)行第一個(gè)yield的值,然后會(huì)記錄執(zhí)行位置,再執(zhí)行第二個(gè)next時(shí)從上一次記錄的位置繼續(xù)執(zhí)行,執(zhí)行中遇到return,會(huì)認(rèn)為函數(shù)的結(jié)束,那么生成器也會(huì)結(jié)束。

讀取生成器的元素 next()

要生成的值并不在生成器中,而需要我們一個(gè)一個(gè)的去生成,可以通過(guò)next()函數(shù)獲得生成器generator()的下一個(gè)返回值:

>>> next(g)

generator保存的是算法,所以每次調(diào)用next(g),就計(jì)算出g的下一個(gè)元素的值,直到計(jì)算到最后一個(gè)元素,沒(méi)有更多的元素時(shí),拋出StopIteration的錯(cuò)誤
但是我們?cè)谑褂胓enerator后,基本上永遠(yuǎn)不會(huì)調(diào)用next(),而是通過(guò)for循環(huán)來(lái)迭代,并且不需要關(guān)心stopIteration的錯(cuò)誤.for循環(huán)要求作用的對(duì)象是迭代對(duì)象,生成器就是一個(gè)迭代對(duì)象。所以可以直接使用for循環(huán)來(lái)取生成器的值

for i in g:
    print(i)

會(huì)在內(nèi)部調(diào)用生成器的next()方法

執(zhí)行迭代器的函數(shù) send(p)

生成器也是函數(shù),可以使用send(p)執(zhí)行生成器函數(shù)并傳遞一個(gè)函數(shù),這個(gè)函數(shù)會(huì)傳遞到y(tǒng)ield的對(duì)象中。

  • 第一次使用send進(jìn)入函數(shù)時(shí)只能傳遞一個(gè)None值,也就是說(shuō)第一次send前如果沒(méi)有next只能發(fā)送None數(shù)據(jù)

b.send(None)

def bar():
    print('before yield')
    count = yield 123
    print(count)
    yield 234
b = bar()

b.send(None)
b.send('abc')

Python 迭代器

在Python中,可以直接作用于for循環(huán)的數(shù)據(jù)類(lèi)型可以分為:
1.集合數(shù)據(jù)類(lèi)型 list、tuple、dict、set、str等
2.生成器generator 包括生成器和帶yield的生成器函數(shù)
這些可以作用于for循環(huán)的對(duì)象統(tǒng)稱(chēng)為可迭代對(duì)象:Iterable
在Python中可以使用isinstance()判斷一個(gè)對(duì)象是否是Iterable對(duì)象

from collections import Iterable

print(isinstance([], Iterable))
print(isinstance({}, Iterable))
print(isinstance((), Iterable))
print(isinstance('123', Iterable))
print(isinstance(100,Iterable))
#True
True
True
True
False
  • 判斷是否是迭代器
    我們可以使用isinstance()判斷一個(gè)對(duì)象是否是迭代器Iterator對(duì)象
from collections import Iterator

print(isinstance([], Iterator))
print(isinstance((), Iterator))
print(isinstance({}, Iterator))
print(isinstance('1111',Iterator))

我們可以看出list dict str tuple等數(shù)據(jù)類(lèi)型雖然是Iterable,卻不是Iterator,So
迭代器一定是可迭代對(duì)象,可迭代對(duì)象不一定是迭代器
那么為什么Python中 list dict str等數(shù)據(jù)類(lèi)型不是Iterator?

因?yàn)镻ython中的Iterator對(duì)象表示的是一個(gè)數(shù)據(jù)流,而Iterator對(duì)象可以被next()函數(shù)調(diào)用并不斷返回下一個(gè)數(shù)據(jù),直到?jīng)]有數(shù)據(jù)時(shí)拋出StopIteration錯(cuò)誤。這個(gè)數(shù)據(jù)流可以看做是一個(gè)有序序列。但我們卻不能提前知道序列的長(zhǎng)度,只能不斷通過(guò)next()函數(shù)實(shí)現(xiàn)按需計(jì)算下一個(gè)數(shù)據(jù),所以Iterator的的計(jì)算是惰性的,只有在需要返回下一給數(shù)據(jù)時(shí)才會(huì)計(jì)算
而Iterator可以表示一個(gè)無(wú)限大的數(shù)據(jù)流。
迭代器滿足的條件:
1有iter方法
2有next方法

  • 可以被next()函數(shù)調(diào)用并不斷返回下一個(gè)值的對(duì)象稱(chēng)為迭代器:Iterator
  • 對(duì)于for循環(huán),在內(nèi)部所完成的三件事:
    1 調(diào)用可迭代對(duì)象的iter方法返回一個(gè)迭代器對(duì)象
    2 不斷調(diào)用迭代器對(duì)象的next方法
    3 捕獲StopIterator異常
for循環(huán)內(nèi)部圖解
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容