在軟件開發(fā)領(lǐng)域中,人們經(jīng)常會用到這一個概念——“設(shè)計模式”(design pattern),它是一種針對軟件設(shè)計的共性問題而提出的解決方案。在一本圣經(jīng)級的書籍《設(shè)計模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)》(1991年,Design Patterns - Elements of Reusable Object-Oriented Software)中,它提出了23種設(shè)計模式。迭代器模式就是其中的一種,在各種編程語言中都得到了廣泛的應(yīng)用。
本文將談?wù)?Python 中的迭代器模式,主要內(nèi)容:什么是迭代器模式、Python 如何實現(xiàn)迭代器模式、itertools 模塊創(chuàng)建迭代器的方法、其它運用迭代器的場景等等,期待與你共同學(xué)習(xí)進步。
1、什么是迭代器模式?
維基百科有如下定義:
迭代器是一種最簡單也最常見的設(shè)計模式。它可以讓用戶透過特定的接口巡訪容器中的每一個元素而不用了解底層的實現(xiàn)?!S基百科
簡單地說,迭代器模式就是一種通用性的可以遍歷容器類型(如序列類型、集合類型等)的實現(xiàn)方式。使用迭代器模式,可以不關(guān)心遍歷的對象具體是什么(如字符串、列表、字典等等),也不需要關(guān)心遍歷的實現(xiàn)算法是什么,它關(guān)心的是從容器中遍歷/取出元素的結(jié)果。
按遍歷方式劃分,迭代器可分為內(nèi)部迭代器與外部迭代器,它們的區(qū)別在于執(zhí)行迭代動作與維持迭代狀態(tài)的不同。
通常而言,迭代器是一次性的,當?shù)^一輪后,再次迭代將獲取不到元素。
2、Python的迭代器模式
由于迭代器模式的使用太常見了,所以大多數(shù)編程語言都給常見的容器類型實現(xiàn)了它,例如 Java 中的 Collection,List、Set、Map等。在 Java 中使用迭代器遍歷 List 可這么寫:
Listlist = new ArrayList<>(); Iterator iterator = list.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); }
ArrayList 類通過自身的 iterator() 方法獲得一個迭代器 iterator,然后由該迭代器實例來落實遍歷過程。
Python 當然也應(yīng)用了迭代器模式,但它的實現(xiàn)思路跟上例卻不太一樣。
首先,Python 認為遍歷容器類型并不一定要用到迭代器,因此設(shè)計了可迭代對象。
list = [1,2,3,4] for i in list: print(i,end=" ") # 1 2 3 4 for i in list: print(i,end=" ") # 1 2 3 4
上例中的 list 是可迭代對象(Iterable),但并不是迭代器(雖然在底層實現(xiàn)時用了迭代器的部分思想)。Python 抓住了迭代器模式的本質(zhì),即是“迭代”,賦予了它極高的地位。
如此設(shè)計的好處顯而易見:(1)寫法簡便,用意直白;(2)可重復(fù)迭代,避免一次性迭代器的缺陷;(3)不需要創(chuàng)建迭代器,減少開銷。
可迭代對象可看作是廣義的迭代器,同時,Python 也設(shè)計了普通意義的狹義的迭代器。
list = [1,2,3,4] it = iter(list) for i in it: print(i,end=" ") # 1 2 3 4 for i in it: print(i,end=" ") # 無輸出
上例中的 iter() 方法會將可迭代對象變成一個迭代器。從輸出結(jié)果可以看出,該迭代器的迭代過程是一次性的。
由此看來,Python 其實是將“迭代器模式”一拆為二來實現(xiàn):一是可迭代思想,廣泛播種于容器類型的對象中,使它們都可迭代;一是迭代器,一種特殊的可迭代對象,承擔(dān)普通意義上的迭代器所特有的迭代任務(wù)。
同時,它還提供了將可迭代對象轉(zhuǎn)化為迭代器的簡易方法,如此安排,真是將迭代器模式的效力發(fā)揮到了極致。
(關(guān)于可迭代對象與迭代器的更多區(qū)別、以及它們的實現(xiàn)原理,請參見《Python進階:迭代器與迭代器切片》)
3、創(chuàng)建迭代器
創(chuàng)建迭代器有如下方式:
(1)iter() 方法,將可迭代對象轉(zhuǎn)化成迭代器;
(2)__iter__() 與 __next__() 魔術(shù)方法,定義類實現(xiàn)這兩個魔術(shù)方法;
(3)itertools 模塊,使用內(nèi)置模塊生成迭代器;
(4)其它創(chuàng)建方法,如 zip() 、map() 、enumerate() 等等。
四類方法各有適用場所,本節(jié)重點介紹 itertools 模塊。它可以創(chuàng)建三類迭代器:無限迭代器、有限迭代器與組合迭代器。
3.1 無限迭代器
count(start=0, step=1) :創(chuàng)建一個從 start (默認值為 0) 開始,以 step (默認值為 1) 為步長的的無限整數(shù)迭代器。
cycle(iterable) :對可迭代對象的元素反復(fù)執(zhí)行循環(huán)。
repeat(object [,times]) :反復(fù)生成 object 至無限,或者到給定的 times 次。
import itertools co = itertools.count() cy = itertools.cycle('ABC') re = itertools.repeat('A', 30) # 注意:請分別執(zhí)行;以下寫法未加終止判斷,只能按 Ctrl+C 退出 for n in co: print(n,end=" ") # 0 1 2 3 4...... for n in cy: print(n,end=" ") # A B C A B C A B...... for n in re: print(n,end=" ") # A A A A A A A A....(30個)
3.2 有限迭代器
以上方法,比較常用的有:chain() 將多個可迭代對象(可以是不同類型)連接成一個大迭代器;compress() 方法根據(jù)真假過濾器篩選元素;groupby() 把迭代器中相鄰的重復(fù)元素挑出來放在一起;islice() 方法返回迭代器切片(用法參見《Python進階:迭代器與迭代器切片》);tee() 方法根據(jù)可迭代對象創(chuàng)建 n 個(默認2個)迭代器副本。
for c in itertools.chain('ABC', [1,2,3]): print(c,end=" ") # 輸出結(jié)果:A B C 1 2 3 for c in itertools.compress('ABCDEF', [1, 1, 0, 1, 0, 1]): print(c,end=" ") # 輸出結(jié)果:A B D F for key, group in itertools.groupby('aaabbbaaccd'): print(key, ':', list(group)) # 輸出結(jié)果: a : ['a', 'a', 'a'] b : ['b', 'b', 'b'] a : ['a', 'a'] c : ['c', 'c'] d : ['d'] itertools.tee('abc', 3) # 輸出結(jié)果:(, , )
3.3 組合迭代器
product() :求解多個可迭代對象的笛卡爾積。
permutations() :求解可迭代對象的元素的排列。
combinations():求解可迭代對象的元素的組合。
for i in itertools.product('ABC', [1,2]): print(i, end=" ") # 輸出結(jié)果:('A', 1) ('A', 2) ('B', 1) ('B', 2) ('C', 1) ('C', 2) for i in itertools.permutations('ABC', 2): print(i, end=" ") # 輸出結(jié)果:('A', 'B') ('A', 'C') ('B', 'A') ('B', 'C') ('C', 'A') ('C', 'B') for i in itertools.combinations('ABC', 2): print(i, end=" ") # 輸出結(jié)果:('A', 'B') ('A', 'C') ('B', 'C') for i in itertools.combinations('ABCD', 3): print(i, end=" ") # 輸出結(jié)果:('A', 'B', 'C') ('A', 'B', 'D') ('A', 'C', 'D') ('B', 'C', 'D')
4、強大的內(nèi)置迭代器方法
迭代器模式的使用場景實在太普遍了,而 Python 也為迭代器的順利使用而提供了很多便利的條件,本節(jié)將介紹相關(guān)的幾個內(nèi)置方法。這些方法非常常用而且強大,是 Python 進階的必會內(nèi)容。
4.1 zip() 方法
zip() 方法可以同時迭代多個序列,并各取一個元素,生成一個可返回元組的迭代器。此迭代器的長度以較短序列的長度保持一致,若想生成較長序列的長度,需要使用 itertools 模塊的 zip_longest() 方法。
import itertools a = [1, 2, 3] b = ['w', 'x', 'y', 'z'] for i in zip(a,b): print(i,end=" ") # (1, 'w') (2, 'x') (3, 'y') # 空缺值以 None 填補 for i in itertools.zip_longest(a,b): print(i,end=" ") # (1, 'w') (2, 'x') (3, 'y') (None, 'z')
4.2 enumerate() 方法
enumerate() 方法接收一個序列類型參數(shù),生成一個可返回元組的迭代器,元組內(nèi)容是下標及其對應(yīng)的元素值。它還可接收一個可選參數(shù),指定下標的起始值,默認是0 。
注意:眾所周知,Python 中序列的索引值從 0 開始,但是,enumerate() 可以達到改變起始索引數(shù)值的效果。
seasons = ['Spring', 'Summer', 'Fall', 'Winter'] for i in enumerate(seasons): print(i,end=" ") #輸出結(jié)果:(0, 'Spring') (1, 'Summer') (2, 'Fall') (3, 'Winter') for i in enumerate(seasons, start=7): print(i,end=" ") #輸出結(jié)果:(7, 'Spring') (8, 'Summer') (9, 'Fall') (10, 'Winter')
4.3 map() 方法
map() 方法的參數(shù)是一個函數(shù)及一個或多個可迭代對象,它會將可迭代對象的元素映射到該函數(shù)中,然后迭代地運行該函數(shù),返回結(jié)果也是一個迭代器。當存在多個可迭代對象參數(shù)時,迭代長度等于較短對象的長度。
def square(x): return x ** 2 l = map(square, [1, 2, 3, 4, 5]) print(list(l)) # 輸出結(jié)果:[1, 4, 9, 16, 25] m = map(lambda x, y: x + y, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10, 2]) print(list(m)) # 輸出結(jié)果:[3, 7, 11, 15, 19]
4.4 filter() 方法
filter() 方法的參數(shù)是一個判斷函數(shù)及一個可迭代對象,遍歷可迭代對象執(zhí)行判斷函數(shù),過濾下判斷為True 的元素,與它相對,若想保留判斷為 False 的元素,可使用 itertoole 模塊的 filterfalse() 方法。
import itertools fi = filter(lambda x: x%2, range(10)) ff = itertools.filterfalse(lambda x: x%2, range(10)) for i in fi: print(i,end=" ") # 輸出結(jié)果:1 3 5 7 9 for i in ff: print(i,end=" ") # 輸出結(jié)果:0 2 4 6 8
5. 小結(jié)
迭代器模式幾乎是 23 種設(shè)計模式中最常用的設(shè)計模式,本文主要介紹了 Python 是如何運用迭代器模式,并介紹了 itertools 模塊生成迭代器的 18 種方法,以及 5 種生成迭代器的內(nèi)置方法。
編輯:hfy
評論
查看更多