1. 程式人生 > >Python進階:設計模式之叠代器模式

Python進階:設計模式之叠代器模式

too python進階 判斷函數 iter class python 不同 本質 sina

在軟件開發領域中,人們經常會用到這一個概念——“設計模式”(design pattern),它是一種針對軟件設計的共性問題而提出的解決方案。在一本聖經級的書籍《設計模式:可復用面向對象軟件的基礎》(1991年,Design Patterns - Elements of Reusable Object-Oriented Software)中,它提出了23種設計模式。叠代器模式就是其中的一種,在各種編程語言中都得到了廣泛的應用。

本文將談談 Python 中的叠代器模式,主要內容:什麽是叠代器模式、Python 如何實現叠代器模式、itertools 模塊創建叠代器的方法、其它運用叠代器的場景等等,期待與你共同學習進步。

1、什麽是叠代器模式?

維基百科有如下定義:

叠代器是一種最簡單也最常見的設計模式。它可以讓用戶透過特定的接口巡訪容器中的每一個元素而不用了解底層的實現。——維基百科

簡單地說,叠代器模式就是一種通用性的可以遍歷容器類型(如序列類型、集合類型等)的實現方式。使用叠代器模式,可以不關心遍歷的對象具體是什麽(如字符串、列表、字典等等),也不需要關心遍歷的實現算法是什麽,它關心的是從容器中遍歷/取出元素的結果。

按遍歷方式劃分,叠代器可分為內部叠代器與外部叠代器,它們的區別在於執行叠代動作與維持叠代狀態的不同。

通常而言,叠代器是一次性的,當叠代過一輪後,再次叠代將獲取不到元素。

2、Python的叠代器模式

由於叠代器模式的使用太常見了,所以大多數編程語言都給常見的容器類型實現了它,例如 Java 中的 Collection,List、Set、Map等。在 Java 中使用叠代器遍歷 List 可以這麽寫:

List<String> list = new ArrayList<>();
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}

ArrayList 類通過自身的 iterator() 方法獲得一個叠代器 iterator,然後由該叠代器實例來落實遍歷過程。

Python 當然也應用了叠代器模式,但它的實現思路跟上例卻不太一樣。

首先,Python 認為遍歷容器類型並不一定要用到叠代器,因此設計了可叠代對象。

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),但並不是叠代器(雖然在底層實現時用了叠代器的部分思想)。Python 抓住了叠代器模式的本質,即是“叠代”,賦予了它極高的地位。

如此設計的好處顯而易見:(1)寫法簡便,用意直白;(2)可重復叠代,避免一次性叠代器的缺陷;(3)不需要創建叠代器,減少開銷。

可叠代對象可看作是廣義的叠代器,同時,Python 也設計了普通意義的狹義的叠代器。

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() 方法會將可叠代對象變成一個叠代器。從輸出結果可以看出,該叠代器的叠代過程是一次性的。

由此看來,Python 其實是將“叠代器模式”一拆為二來實現:一是可叠代思想,廣泛播種於容器類型的對象中,使它們都可叠代;一是叠代器,一種特殊的可叠代對象,承擔普通意義上的叠代器所特有的叠代任務。 同時,它還提供了將可叠代對象轉化為叠代器的簡易方法,如此安排,真是將叠代器模式的效力發揮到了極致。(關於可叠代對象與叠代器的更多區別、以及它們的實現原理,請參見《Python進階:叠代器與叠代器切片》)

3、創建叠代器

創建叠代器有如下方式:(1)iter() 方法,將可叠代對象轉化成叠代器;(2)__iter__()__next__() 魔術方法,定義類實現這兩個魔術方法;(3)itertools 模塊,使用內置模塊生成叠代器;(4)其它創建方法,如 zip() 、map() 、enumerate() 等等。

四類方法各有適用場所,本節重點介紹 itertools 模塊。它可以創建三類叠代器:無限叠代器、有限叠代器與組合叠代器。

3.1 無限叠代器

count(start=0, step=1) :創建一個從 start (默認值為 0) 開始,以 step (默認值為 1) 為步長的的無限整數叠代器。

cycle(iterable) :對可叠代對象的元素反復執行循環。

repeat(object [,times]) :反復生成 object 至無限,或者到給定的 times 次。

import itertools
co = itertools.count()
cy = itertools.cycle(‘ABC‘)
re = itertools.repeat(‘A‘, 30)

# 註意:請分別執行;以下寫法未加終止判斷,只能按 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() 方法根據真假過濾器篩選元素;groupby() 把叠代器中相鄰的重復元素挑出來放在一起;islice() 方法返回叠代器切片(用法參見《Python進階:叠代器與叠代器切片》);tee() 方法根據可叠代對象創建 n 個(默認2個)叠代器副本。

for c in itertools.chain(‘ABC‘, [1,2,3]):
print(c,end=" ")
# 輸出結果:A B C 1 2 3

for c in itertools.compress(‘ABCDEF‘, [1, 1, 0, 1, 0, 1]):
print(c,end=" ")
# 輸出結果:A B D F

for key, group in itertools.groupby(‘aaabbbaaccd‘):
print(key, ‘:‘, list(group))
# 輸出結果:
a : [‘a‘, ‘a‘, ‘a‘]
b : [‘b‘, ‘b‘, ‘b‘]
a : [‘a‘, ‘a‘]
c : [‘c‘, ‘c‘]
d : [‘d‘]

itertools.tee(‘abc‘, 3)
# 輸出結果:(<itertools._tee at 0x1fc72c08108>,
<itertools._tee at 0x1fc73f91d08>,
<itertools._tee at 0x1fc73efc248>)

3.3 組合叠代器

product() :求解多個可叠代對象的笛卡爾積。

permutations() :求解可叠代對象的元素的全排列。

combinations():求解可叠代對象的元素的組合。

for i in itertools.product(‘ABC‘, [1,2]):
print(i, end=" ")
# 輸出結果:(‘A‘, 1) (‘A‘, 2) (‘B‘, 1) (‘B‘, 2) (‘C‘, 1) (‘C‘, 2)

for i in itertools.permutations(‘ABC‘, 2):
print(i, end=" ")
# 輸出結果:(‘A‘, ‘B‘) (‘A‘, ‘C‘) (‘B‘, ‘A‘) (‘B‘, ‘C‘) (‘C‘, ‘A‘) (‘C‘, ‘B‘)

for i in itertools.combinations(‘ABC‘, 2):
print(i, end=" ")
# 輸出結果:(‘A‘, ‘B‘) (‘A‘, ‘C‘) (‘B‘, ‘C‘)

for i in itertools.combinations(‘ABCD‘, 3):
print(i, end=" ")
# 輸出結果:(‘A‘, ‘B‘, ‘C‘) (‘A‘, ‘B‘, ‘D‘) (‘A‘, ‘C‘, ‘D‘) (‘B‘, ‘C‘, ‘D‘)

4、強大的內置叠代器方法

叠代器模式的使用場景實在太普遍了,而 Python 也為叠代器的順利使用而提供了很多便利的條件,本節將介紹相關的幾個內置方法。這些方法非常常用而且強大,是 Python 進階的必會內容。

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() 方法接收一個序列類型參數,生成一個可返回元組的叠代器,元組內容是下標及其對應的元素值。它還可接收一個可選參數,指定下標的起始值,默認是0 。

註意:眾所周知,Python 中序列的索引值從 0 開始,但是,enumerate() 可以達到改變起始索引數值的效果。

seasons = [‘Spring‘, ‘Summer‘, ‘Fall‘, ‘Winter‘]

for i in enumerate(seasons):
print(i,end=" ")
#輸出結果:(0, ‘Spring‘) (1, ‘Summer‘) (2, ‘Fall‘) (3, ‘Winter‘)

for i in enumerate(seasons, start=7):
print(i,end=" ")
#輸出結果:(7, ‘Spring‘) (8, ‘Summer‘) (9, ‘Fall‘) (10, ‘Winter‘)

4.3 map() 方法

map() 方法的參數是一個函數及一個或多個可叠代對象,它會將可叠代對象的元素映射到該函數中,然後叠代地運行該函數,返回結果也是一個叠代器。當存在多個可叠代對象參數時,叠代長度等於較短對象的長度。

def square(x):
return x ** 2

l = map(square, [1, 2, 3, 4, 5])
print(list(l))
# 輸出結果:[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))
# 輸出結果:[3, 7, 11, 15, 19]

4.4 filter() 方法

filter() 方法的參數是一個判斷函數及一個可叠代對象,遍歷可叠代對象執行判斷函數,過濾下判斷為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=" ")
# 輸出結果:1 3 5 7 9

for i in ff:
print(i,end=" ")
# 輸出結果:0 2 4 6 8

5. 小結

叠代器模式幾乎是 23 種設計模式中最常用的設計模式,本文主要介紹了 Python 是如何運用叠代器模式,並介紹了 itertools 模塊生成叠代器的 18 種方法,以及 5 種生成叠代器的內置方法。

相關鏈接:

itertools模塊文檔:http://t.cn/R6cGtfw

Python進階:叠代器與叠代器切片

Python進階:全面解讀高級特性之切片!

-----------------

本文原創並首發於微信公眾號【Python貓】,後臺回復“愛學習”,免費獲得20+本精選電子書。

Python進階:設計模式之叠代器模式