亚洲国产欧美一区二区三区f,亚洲A∨精品永久无码青草网,亚洲 暴爽 av人人爽日日碰,亚洲AV永久无码精心天堂久久_无码

系統城裝機大師 - 唯一官網:www.outletmksalestore.com!

當前位置:首頁 > 腳本中心 > python > 詳細頁面

python中的迭代器和生成器

時間:2020-03-14來源:電腦系統城作者:電腦系統城

在我們學習迭代器和生成器之前的時候,我們要先搞清楚幾個概念:

  • 「迭代協議:」 有__next__方法會前進道下一個結果,而且在一系列結果的末尾時,會引發StopIteration異常的對象.
  • 「可迭代對象:」 實現了__iter__方法的對象
  • 「迭代器:」 實現了__iter____next__方法的對象
  • 「生成器:」 通過生成器表達式或者yeild關鍵字實現的函數.

這里不太好理解,我們借用一個圖

可迭代對象

需要注意的是可迭代對象不一定是迭代器.比如列表類型和字符串類型都是可迭代對象,但是他們都不是迭代器.

In [1]: L1 = [1,2,3,4]

In [2]: type(L1)
Out[2]: list

In [3]: L1_iter=L1.__iter__()

In [4]: type(L1_iter)
Out[4]: list_iterator

但是對于容器以及文件這樣的可迭代對象來說的話,他們都實現了一個__iter__方法. 這個方法可以返回一個迭代器.

迭代器中的__next__方法,next()方法和for語句

首先迭代器中都實現了__next__()方法. 我們可以直接調用迭代器的__next__方法來得到下一個值. 比如:

In [10]: L1_iter.__next__()
Out[10]: 1

In [11]: next(L1_iter)
Out[11]: 2

注意這里,next()方法也是去調用迭代器內置的__next__方法. 所以這兩種操作是一樣的. 但是在日常使用的時候,我們不會直接去調用next()方法來使用生成器.

更多的操作是通過for語句來使用一個生成器.

就下面這兩段代碼來看,其作用上是等效的.

L1 = [1, 2, 3, 4]

for x in L1:
    print(x, end="  ")

print("\nthe same result of those two statements!")

L1_iter = L1.__iter__()
while True:
    try:
        x = L1_iter.__next__()
        print(x, end="  ")
    except StopIteration:
        break

但是實際上,使用for語句在運行速度可能會更快一點. 因為迭代器在Python中是通過C語言實現的. 而while的方式則是以Python虛擬機運行Python字節碼的方式來執行的.

畢竟...你大爺永遠是你大爺. C語言永遠是你大爺...

列表解析式

列表解析式或者又叫列表生成式,這個東西就比較簡單了. 舉個簡單的例子,比如我們要定義一個1-9的列表. 我們可以寫L=[1,2,3,4,5,6,78,9] 同樣我們也可以寫L=[x for x in range(10)]

再舉一個簡單的例子,我們現在已經有一個列表L2=[1,2,3,4,5] 我們要得到每個數的平方的列表. 那么我們有兩種做法:

L2 = [1, 2, 3, 4, 5]

# statement1
for i in range(len(L2)):
    L2[i] = L2[i]*L2[i]
#statement2
L3 = [x*x for x in L2]

顯然從代碼簡潔渡上來說 第二種寫法更勝一籌. 而且它的運算速度相對來說會更快一點(往往速度會快一倍.P.S.書上的原話,我沒有驗證...). 因為列表解析式式通過生成器來構造的,他們的迭代是python解釋器內部以C語言的速度來運行的. 特別是對于一些較大的數據集合,列表解析式的性能優點更加突出.

列表解析式還有一些高端的玩法. 比如可以與if語句配合使用:

L4 = [1, 2, 3, 4, 5, 6, 7, 8, 9]

L5 = [x for x in L4 if x % 2 == 0]

還可以使用for語句嵌套;

L6=[1,2,3,4,5]
L7=['a','b','c','d','e']

L8=[str(x)+y for x in L6 for y in L7]

或者可以寫的更長

L9=[(x,y) for x in range(5) if x % 2 ==0 for y in range(5) if y %2 ==1]

一個更復雜的例子

M = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]
print(M[1][1])
print(M[1])
print([row[1] for row in M])
print([M[row][1] for row in (0,1,2)])
print([M[i][i] for i in range(len(M))])

同樣的,我們可以通過for語句來實現上述的功能. 但是列表解析式想對而言會更加簡潔.

另外map函數比等效的for循環要更快,而列表解析式往往會比map調用還要快一點.

python3中新的可迭代對象

python3相比如python2.x來說,它更強調迭代. 除了文件,字典這樣的內置類型相關的迭代外. 字典方法keys,values都在python3中返回可迭代對象. 就像map,range,zip方法一樣. 它返回的并不是一個列表. 雖然從速度和內存占用上更有優勢,但是有時候我們不得不使用list()方法使其一次性計算所有的結果.

range迭代器

In [12]: R=range(10)

In [13]: R
Out[13]: range(0, 10)

In [14]: I = iter(R)

In [15]: next(I)
Out[15]: 0

In [16]: R[5]
Out[16]: 5

In [17]: len(R)
Out[17]: 10

In [18]: next(I)
Out[18]: 1

range的話,僅支持迭代,len()和索引. 不支持其他的序列操作. 所以如果需要更多的列表工具的話,使用list()...

map,zip和filter迭代器

和range類似, map,zip和filter在python3.0中也轉變成了迭代器以節約內存空間. 但是它們和range又不一樣.(確切來說是range和它們不一樣) 它們不能在它們的結果上擁有在那些結果中保持不同位置的多個迭代器.(第四版書上原話,看看這叫人話嗎...)

翻譯一下就是,map,zip和filter返回的都是正經迭代器,不支持len()和索引. 以map為例做個對比.

In [20]: map_abs = map(abs,[1,-3,4])

In [21]: M1 = iter(map_abs)

In [22]: M2=iter(map_abs)

In [23]: next(M1)
Out[23]: 1

In [24]: next(M2)
Out[24]: 3

而range不是正經的迭代器. 它支持在其結果上創建多個活躍的迭代器.

In [25]: R=range(10)

In [26]: r1 = iter(R)

In [27]: r2=iter(R)

In [28]: next(r1)
Out[28]: 0

In [29]: next(r2)
Out[29]: 0

字典中的迭代器

同樣的,python3中字典的keys,values和items方法返回的都是可迭代對象.而非列表.

In [30]: D = dict(a=1,b=2,c=3)

In [31]: D
Out[31]: {'a': 1, 'b': 2, 'c': 3}

In [32]: K = D.keys()

In [33]: K
Out[33]: dict_keys(['a', 'b', 'c'])

In [34]: next(K)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-34-02c2ef8731e9> in <module>
----> 1 next(K)

TypeError: 'dict_keys' object is not an iterator

In [35]: i = iter(K)

In [36]: next(i)
Out[36]: 'a'

In [37]: for k in D.keys(): print(k,end=' ')

同樣的,我們可以利用list()函數來顯式的把他們變成列表. 另外,python3中的字典仍然有自己的迭代器. 它返回連續的見. 因此在遍歷的時候,無需顯式的調用keys().

In [38]: for key in D: print(key,end=' ')
a b c

生成器

生成器可以說是迭代器的一個子集. 有兩種創建方法:

  • 生成器函數: 編寫常規的以def為關鍵字的函數,使用yield關鍵字返回結果. 在每個結果之間掛起和繼續他們的狀態.
  • 生成器表達式: 類似前面所說的列表解析式.

生成器函數關鍵字 yeild

「狀態掛起」

和返回一個值并且退出的常規函數不同,生成器函數自動在生成值得時刻掛起并繼續函數的執行. 它在掛起時會保存包括整個本地作用域在內的所有狀態. 在恢復執行時,本地變量信息依舊可用.

生成器函數使用yeild語句來掛起函數并想調用者發送回一個值.之后掛起自己. 在恢復執行的時候,生成器函數會從它離開的地方繼續執行.

「生成器函數的應用」

def gensquares(num):
    for i in range(num):
        yield i**2

for i in gensquares(5):
    print(i)

同樣的,生成器其實也是實現了迭代器協議. 提供了內置的__next__方法.

上面這個例子如果我們要改寫為普通函數的話,可以寫成如下的樣子.

def buildsquares(num):
    res = []
    for i in range(num):
        res.append(i**2)
    return res

for i in buildsquares(5):
    print(i)

看上去實現的功能都是一樣的. 但是區別在于 生成器的方式產生的是一個惰性計算序列. 在調用時才進行計算得出下一個值. 而第二種常規函數的方式,是先計算得出所有結果返回一個列表. 從內存占用的角度來說,生成器函數的方式更優一點.

生成器表達式

與列表解析式差不多. 生成器表達式用來構造一些邏輯相對簡單的生成器. 比如

g = (x**2 for x in range(4))

在使用時可以通過next()函數或者for循環進行調用.

實戰:改寫map和zip函數

改寫map函數

「一年級版本:」

def mymap(func,*seqs):
    res=[]
    print(list(zip(*seqs)))
    for args in zip(*seqs):
        res.append(func(*args))
    return res

「二年級版本:」

def mymap(func,*seqs):    
    return [func(*args) for args in zip(*seqs)]

「三年級版本:」

def mymap(func,*seqs):
    res=[]
    for args in zip(*seqs):
        yield func(*args)


print(list(mymap(abs,[-1,-2,1,2,3])))
print(list(mymap(pow,[1,2,3],[2,3,4,5])))

「小學畢業班版本」

def mymap(func,*seqs):
    return (func(*args) for args in zip(*seqs))

改寫zip函數

「一年級版本」

def myzip(*seqs):
    seqs = [list(S) for S in seqs]
    print(seqs)
    res = []
    while all(seqs):
        res.append(tuple(S.pop(0) for S in seqs))
    return res


print(myzip('abc', 'xyz'))
?

知識點: all()函數和any()函數. all()函數,如果可迭代對象中的所有元素都為True或者可迭代對象為None. 則返回True. any()函數,可迭代對象中的任一元素為True則返回True.,如果迭代器為空,則返回False.

?

「二年級版本」

def myzip(*seqs):
    seqs = [list(S) for S in seqs]
    while all(seqs):
        yield tuple(S.pop(0) for S in seqs)

print(list(myzip('abc', 'xyz')))
分享到:

相關信息

系統教程欄目

欄目熱門教程

人氣教程排行

站長推薦

熱門系統下載

亚洲国产欧美一区二区三区f,亚洲A∨精品永久无码青草网,亚洲 暴爽 av人人爽日日碰,亚洲AV永久无码精心天堂久久_无码 日本少妇又色又爽又高潮