Python迭代和解析(2):迭代初探

来源:https://www.cnblogs.com/f-ck-need-u/archive/2019/01/12/10259746.html
-Advertisement-
Play Games

在Python中支持兩種迴圈格式:while和for。這兩種迴圈的類型不同: while是通過條件判斷的真假來迴圈的 for是通過in的元素存在性測試來迴圈的 更通俗地說,while是普通的步進迴圈,for是迭代遍歷。 for的關鍵字在於"迭代"和"遍歷"。首先要有容器數據結構(如列表、字元串)存儲 ...


解析、迭代和生成系列文章:https://www.cnblogs.com/f-ck-need-u/p/9832640.html


在Python中支持兩種迴圈格式:while和for。這兩種迴圈的類型不同:

  • while是通過條件判斷的真假來迴圈的
  • for是通過in的元素存在性測試來迴圈的

更通俗地說,while是普通的步進迴圈,for是迭代遍歷。

for的關鍵字在於"迭代"和"遍歷"。首先要有容器數據結構(如列表、字元串)存儲一些元素供迭代、遍歷,然後每次取下一個元素通過in來測試元素的存在性(從容器中取了元素為何還要測試?因為容器可能會在迭代過程中臨時發生改變),每次取一個,依次取下去,直到所有元素都被迭代完成,就完成了遍歷操作。

這種迭代模式是一種惰性的工作方式。當要掃描記憶體中放不下的大數據集時,需要找到一種惰性獲取數據項的方式,即按需一次獲取一個數據項,而不是一次性收集全部數據。從此可以看出這種迭代模式最顯著的優點是"記憶體占用少",因為它從頭到尾迭代完所有數據的過程中都只需占用一個元素的記憶體空間。

Python中的迭代和解析和for都息息相關,本文先初探迭代。

內置類型的迭代

for迴圈可以迭代列表、元組、字元串(str/bytes/bytearray)、集合、字典、文件等類型。

>>> for i in [1,2,3,4]: print(i * 2,end=" ")
...
2 4 6 8

>>> for i in (1,2,3,4): print(i * 2,end=" ")
...
2 4 6 8

>>> for i in "abcd": print(i * 2,end=" ")
...
aa bb cc dd

>>> D=dict(a=1,b=2,c=3)
>>> for k in D:print("%s -> %s" % (k, D[k]))
...
a -> 1
b -> 2
c -> 3

for迴圈其實比這更加通用。在Python中,只要是可迭代對象,或者更通俗地說是從左至右掃描對象的工具都可以進行這些迭代操作,這些工具有for、in成員測試、解析、map/zip等內置函數等。

關於什麼是可迭代對象,後文會詳細解釋。

文件迭代操作

要讀取一個文件有很多種方式:按位元組數讀取、按行讀取、按段落讀取、一次性全部讀取等等。如果不是深入的操作文件數據,按行讀、寫是最通用的方式。

以下是下麵測試時使用的文件a.txt的內容:

first line
second line
third line

在Python中,readline()函數可以一次讀取一行,且每次都是前進式的讀取一行,讀到文件結尾的時候會返回空字元串。

>>> f = open('a.txt')
>>> f.readline()
'first line\n'
>>> f.readline()
'second line\n'
>>> f.readline()
'third line\n'
>>> f.readline()
''

readline()的操作就像是有一個指針,每次讀完一行就將指針指向那一行的後面做下標記,以便下次能從這裡開始繼續向後讀取一行。

除了readline(),open()打開的文件對象還有另一種方式__next__()可以一次向前讀取一行,只不過__next__()在讀取到文件結尾的時候不是返回空字元串,而是直接拋出迭代異常:

>>> f = open("a.txt")
>>> f.__next__()
'first line\n'
>>> f.__next__()
'second line\n'
>>> f.__next__()
'third line\n'
>>> f.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

內置函數next()會自動調用__next__(),也能進行迭代:

>>> f = open("a.txt")
>>> next(f)
'first line\n'
>>> next(f)
'second line\n'
>>> next(f)
'third line\n'
>>> next(f)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

要想再次讀取這個文件,只能先重置這個指針,比如重新打開這個文件可以重置指針。

open()打開的文件是一個可迭代對象,它有__next__(),它可以被for/in等迭代工具來操作,例如:

>>> 'first line\n' in open('a.txt')
True

所以更好的按行讀取文件的方式是for line in open('file'),不用刻意使用readline()等函數去讀取。

>>> for line in open('a.txt'):
...     print(line,end='')
...
first line
second line
third line

上面的print()設置了end='',因為讀取每一行時會將換行符也讀入,而print預設是自帶換行符的,所以這裡要禁止print的終止符,否則每一行後將多一空行。

上面使用for line in open('a.txt')的方式是最好的,它每次只讀一行到記憶體,在需要讀下一行的時候再去文件中讀取,直到讀完整個文件也都只占用了一行數據的記憶體空間。

也可以使用while去讀取文件,並:

>>> f=open('a.txt')
>>> while True:
...     line = f.readline()
...     if not line: break
...     print(line,end='')
...
first line
second line
third line

在Python中,使用for一般比while速度更快,它是C寫的,而while是Python虛擬機的解釋代碼。而且,for一般比while要更簡單,而往往Python中的簡單就意味著高效。

此外,還可以使用readlines()函數(和readline()不同,這是複數形式),它表示一次性讀取所有內容到一個列表中,每一行都是這個大列表的一個元素。

>>> lines = open('a.txt').readlines()
>>> lines
['first line\n', 'second line\n', 'third line\n']

因為存放到列表中了,所以也可以迭代readlines()讀取的內容:

>>> for line in open('a.txt').readlines():
...     print(line,end='')
...
first line
second line
third line

這種一次性全部讀取的方式在大多數情況下並非良方,如果是一個大文件,它會占用大量記憶體,甚至可能會因為記憶體不足而讀取失敗。

但並非必須要選擇for line in open('a.txt')的方式,因為有些時候必須載入整個文件才能進行後續的操作,比如要排序文件,必須要擁有文件的所有數據才能進行排序。而且對於小文件來說,一次性讀取到一個列表中操作起來可能會更加方便,因為列表對象有很多好用的方法。所以,不能一概而論地選擇for line in open('a.txt')

手動迭代

Python 3.X提供了一個內置函數next(),它會自動調用對象的__next__(),所以藉助它可以進行手動迭代。

>>> f=open('a.txt')
>>> next(f)
'first line\n'
>>> next(f)
'second line\n'
>>> next(f)
'third line\n'
>>> next(f)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

可迭代對象、迭代協議和迭代工具的工作流程

這裡只是解釋這幾個概念和__iter__()__next__(),在後面會手動編寫這兩個方法來自定義迭代對象。

什麼是迭代協議

參考手冊:https://docs.python.org/3.7/library/stdtypes.html#iterator-types

只要某個類型(類)定義了__iter__()__next__()方法就表示支持迭代協議。

__iter__()需要返回一個可迭代對象。只要定義了__iter__()就表示能夠通過for/in/map/zip等迭代工具進行對應的迭代,也可以手動去執行迭代操作

for x in Iterator
X in Iterator

同時,可迭代對象還可以作為某些函數參數,例如將可迭代對象構建成一個列表list(Iterator)來查看這個可迭代對象會返回哪些數據:

L = list(Iterator)

需要註意的是,for/in/map/zip等迭代工具要操作的對象並不一定要實現__iter__(),實現了__getitem__()也可以。__getitem__()是數值索引迭代的方式,它的優先順序低於__iter__()

__next__()方法用於向前一次返回一個結果,並且在前進到結尾的地方觸發StopIteration異常。

再次說明,只要實現了這兩個方法的類型,就表示支持迭代協議,可以被迭代。

例如open()的文件類型:

>>> f=open('a.txt')
>>> dir(f)
[... '__iter__', ... '__next__', ...]

但如果看下列表類型、元組、字元串等容器類型的屬性列表,會發現沒有它們只有__iter__(),並沒有__next__()

>>> dir(list)
[... '__iter__', ...]

>>> dir(tuple)
[... '__iter__', ...]

>>> dir(str)
[... '__iter__', ...']

>>> dir(set)
[... '__iter__', ...]

>>> dir(dict)
[... '__iter__', ...]

但為什麼它們能進行迭代呢?繼續看下文"可迭代對象"的解釋。

什麼是迭代對象和迭代器

對於前面的容器類型(list/set/str/tuple/dict)只有__iter__()而沒有__next__(),但卻可以進行迭代操作的原因,是這些容器類型的__iter__()返回了一個可迭代對象,而這些可迭代對象才是真的支持迭代協議、可進行迭代的對象。

>>> L=[1,2,3,4]
>>> L_iter = L.__iter__()

>>> L_iter
<list_iterator object at 0x000001E53A105400>

>>> dir(L_iter)
[... '__iter__', ... '__next__', ...]

>>> L.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute '__next__'

>>> L_iter.__next__()
1
>>> L_iter.__next__()
2
>>> L_iter.__next__()
3
>>> L_iter.__next__()
4

所以,對於容器類型,它們是通過__iter__()來返回一個迭代對象,然後這個可迭代對象需要支持迭代協議(有__iter__()__next__()方法)。

也就是說,所謂的迭代對象是通過__iter__()來返回的。迭代對象不一定可迭代,只有支持迭代協議的迭代對象才能稱為可迭代對象

迭代器則是迭代對象的一種類型統稱,只要是可迭代對象,都可以稱為迭代器。所以,一般來說,迭代器和可迭代對象是可以混用的概念。但嚴格點定義,迭代對象是iter()返回的,迭代器是__iter__()返回的,所以它們的關係是:從迭代對象中獲取迭代器(可迭代對象)。

如果要自己定義迭代對象類型,不僅需要返回可迭代對象,還需要這個可迭代對象同時實現了__iter__()__next__()

正如open()返回的類型,它有__iter__()和__next__(),所以它支持迭代協議,可以被迭代。再者,它的__iter__()返回的是自身,而自身又實現了這兩個方法,所以它是可迭代對象:

>>> f = open('a.txt')
>>> f.__iter__() is f
True

所以,如果想要知道某個對象是否可迭代,可以直接調用iter()來測試,如果它不拋出異常,則說明可迭代(儘管還要求實現__next__())。

迭代工具的工作流程

像for/in/map/zip等迭代工具,它們的工作流程大致遵循這些過程(並非一定如此):

  1. 在真正開始迭代之前,首先會通過iter(X)內置函數獲取到要操作的迭代對象Y
    • 例如it = iter([1,2,3,4])
    • iter(X)會調用X的__iter__(),前面說過這個方法要求返回迭代對象
    • 如果沒有__iter__(),則iter()轉而調用__getitem__()來進行索引迭代
  2. 獲取到迭代對象後,開始進入迭代過程。在迭代過程中,每次都調用next(Y)內置函數來生成一個結果,而next()會自動調用Y的__next__()

如果類型對象自身就實現了__iter__()__next__(),則這個類型的可迭代對象就是自身。就像open()返回的文件類型一樣。

如果自身只是實現了__iter__()而沒有__next__(),那麼它的__iter__()就需要返回實現了__iter__()__next__()的類型對象。這種類型的對象自身不是迭代器,就像內置的各種可迭代容器類型一樣。

關於iter(), __iter__(), next(), __next__(),它們兩兩的作用是一致的,只不過基於類設計的考慮,將__iter__()__next__()作為了通用的類型對象屬性,而額外添加了iter()和next()來調用它們。

for/map/in/zip等迭代工具是自動進行迭代的,但既然理解了可迭代對象,我們也可以手動去迴圈迭代:

>>> L=[1,2,3,4]
>>> for i in L:print(i,end=" ")
...
1 2 3 4

L = [1,2,3,4]
I = iter(L)
while True:
    try:
        x = next(I)
    except StopIteration:
        break
    print(x,end=" ")

註意:

  1. 每一個迭代對象都是一次性資源,迭代完後就不能再次從頭開始迭代,如果想要再次迭代,必須使用iter()重新獲取迭代對象
  2. 每次迭代時,都會標記下當前所迭代的位置,以便下次從下一個指針位置處繼續迭代

可迭代對象示例:range和enumerate

range()返回的內容是一個可迭代對象,作為可迭代對象,可以進行上面所描述的一些操作。

>>> 3 in range(5)
True

>>> for i in range(5):print(i,end=" ")
...
0 1 2 3 4

>>> list(range(5))
[0, 1, 2, 3, 4]

>>> R = range(5)
>>> I = iter(R)
>>> next(I)
0
>>> next(I)
1
>>> next(I)
2
>>> next(I)
3
>>> next(I)
4
>>> next(I)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

enumerate()返回的也是可迭代對象:

>>> E = enumerate('hello')
>>> E
<enumerate object at 0x000001EF6BFD1F78>
>>> I = iter(E)
>>> next(I)
(0, 'h')
>>> next(I)
(1, 'e')
>>> next(I)
(2, 'l')
>>> next(I)
(3, 'l')
>>> next(I)
(4, 'o')
>>> next(I)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

可迭代對象實例:字典的可迭代視圖

字典自身有__iter__(),所以dict也是可迭代的對象,只不過它所返回的可迭代對象是dict的key。

>>> D = dict(one=1,two=2,three=3,four=4)
>>> I = iter(D)
>>> next(I)
'one'
>>> next(I)
'two'
>>> next(I)
'three'
>>> next(I)
'four'
>>> next(I)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

除此之外,dict還支持其它可迭代的字典視圖keys()、values()、items()。

>>> hasattr(D.keys(),"__iter__")
True
>>> hasattr(D.values(),"__iter__")
True
>>> hasattr(D.items(),"__iter__")
True

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

-Advertisement-
Play Games
更多相關文章
  • range range()是一個內置函數,它返回一個數字序列,功能和Linux下的seq命令差不多。 range()返回的是一個可迭代對象(迭代器),可以被迭代工具for/in/map/zip等操作。 作為一個可迭代對象,它還支持len()操作和索引操作: 如果想要實現其它功能,可以將其轉換為lis ...
  • 當@ResponseBody放到Controller類上,改Controller中所有的方法返回的數據都會以json格式直接寫給瀏覽器。 ...
  • 1.線程: 一個進程可以有多個線程,共用一個進程的資源; 2.進程線程的區別: 進程是資源分配的最小單位,線程是程式執行的最小單位 3.python中線程模塊threading, 提供的類: Thread, Lock, Rlock, Semaphore, Event, 等等 4.線程的創建方式 6. ...
  • 本篇和大家分享的是springcloud-hystrix熔斷器,其主要功能是對某模塊調用失敗做斷路和降級,簡單點就當某個模塊程式出問題了並達到某閾值就限制後面請求,並降級的方式提供一個預設返回數據。最近在琢磨hystrix源碼,琢磨思路寫一個自己的簡易熔斷器,希望大家後期關註。 springclou ...
  • 0x01: 部分參考:https://www.cnblogs.com/edwardsun/p/4421773.html match(string[, pos[, endpos]]) | re.match(pattern, string[, flags]): 這個方法將從string的pos下標處起嘗 ...
  • 背景:聽說transient Java高階語法是挺進BAT必經之路。 transient: Java中transient 關鍵字的作用,簡單的說就是讓某些被修飾的成員屬性變數不被序列化。 這又扯到了序列化和反序列化: Java中對象的序列化是指將對象轉換成以位元組序列的形式來表示,這些位元組序列包含了對 ...
  • [TOC] 查看可用命令 直接在終端中輸入 go help 即可顯示所有的 go 命令以及相應命令功能簡介,主要有下麵這些: build: 編譯包和依賴 clean: 移除對象文件 doc: 顯示包或者符號的文檔 env: 列印go的環境信息 bug: 啟動錯誤報告 fix: 運行go tool f ...
  • 以下內容需要掌握: Python3 以及前端:HTML,CSS,jQuery,BootStrap,Django,JavaScript 開啟Django新項目: 1,settings.py 資料庫選擇: ①sqlite3(Django自帶的資料庫:文件式資料庫):我們這裡用sqlite3,下麵MySQ ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...