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

来源:https://www.cnblogs.com/pythonista/archive/2019/01/02/10211661.html
-Advertisement-
Play Games

在軟體開發領域中,人們經常會用到這一個概念——“設計模式”(design pattern),它是一種針對軟體設計的共性問題而提出的解決方案。在一本聖經級的書籍《設計模式:可復用面向對象軟體的基礎》(1991年,Design Patterns - Elements of Reusable Object ...


 

在軟體開發領域中,人們經常會用到這一個概念——“設計模式”(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+本精選電子書。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 題意 "題目鏈接" Sol 二維數點板子題 首先把詢問拆成四個矩形 然後離散化+樹狀數組統計就可以了 cpp include define int long long define LL long long using namespace std; const int MAXN = 1e6 + 10 ...
  • Python 可以通過 threading module 來創建新的線程,然而在創建線程的父線程關閉之後,相應的子線程可能卻沒有關閉,這可能是因為代碼中沒有使用 函數。接下來,使用一個例子來說明: ...
  • Dart是一個面向對象的語言,同時增加了混入(mixin)繼承的特性。對象都是由類初始化生成的,所有的類都由Object對象繼承。混入繼承意味著儘管所有類(除了Object類)只有一個父類,但是類的代碼體可以在多個類中重覆使用。(個人理解:mixin,extends,implements,exte ...
  • 什麼是閉包 #定義一個函數 def test(number): #在函數內部再定義一個函數,並且這個函數用到了外邊函數的變數,那麼將這個函數以及用到的一些變數稱之為閉包 def test_in(number_in): print("in test_in 函數, number_in is %d"%nu ...
  • 可迭代對象 以直接作用於 for 迴圈的數據類型有以下幾種: 一類是集合數據類型,如 list 、 tuple 、 dict 、 set 、 str 等; 一類是 generator ,包括生成器和帶 yield 的generator function。 這些可以直接作用於 for 迴圈的對象統稱為 ...
  • 運行結果: 結論:非守護線程結束之後jvm退出,守護線程沒有要守護的線程,也進行退出 守護線程和用戶線程唯一的區別在於: 補充說明: 定義:守護線程--也稱“服務線程”,在沒有用戶線程可服務時會自動離開。 優先順序:守護線程的優先順序比較低,用於為系統中的其它對象和線程提供服務。 設置:通過setDae ...
  • 由來: Java是由Sun Microsystems公司於1995年5月推出的Java面向對象程式設計語言和Java平臺的總稱。由James Gosling和同事們共同研發,併在1995年正式推出。 特性: 1.簡單性 :Java吸收了C++語言的各種優點,使得大多數程式員很容易學習和使用。另一方面 ...
  • 1.什麼是進程、線程 進程可以簡單的理解為應用程式。多線程,相當於多個人共同完成一件事情,每個線程,獨立完成一件任務。因此,一個進程至少有一個線程,一個線程不能獨立存在,它必須是進程的一部分。 進程:每個獨立運行著的程式。具有獨立的記憶體空間和系統資源。(建大廈) 線程:是一個進程內部的一條執行路徑。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...