Python標準庫---十一、內置類型:迭代器類型、序列類型(list-typle-range)

上一篇文章: Python標準庫---十、內置類型:數字類型
下一篇文章: Python標準庫---十二、內置類型:文本序列類型(str)

## 迭代器類型
Python 支持在容器中進行迭代的概念。 這是經過使用兩個單獨方法來實現的;它們被用於容許用戶自定義類對迭代的支持。 將在下文中詳細描述的序列老是支持迭代方法。編程

容器對象要提供迭代支持,必須定義一個方法:segmentfault

container.__iter__()

返回一個迭代器對象。 該對象須要支持下文所述的迭代器協議。 若是容器支持不一樣的迭代類型,則能夠提供額外的方法來專門地請求不一樣迭代類型的迭代器。 (支持多種迭代形式的對象的例子有同時支持廣度優先和深度優先遍歷的樹結構。) 此方法對應於 Python/C API 中 Python 對象類型結構體的 tp_iter 槽位。app

迭代器對象自身須要支持如下兩個方法,它們共同組成了 迭代器協議:函數

iterator.__iter__()

返回迭代器對象自己。 這是同時容許容器和迭代器配合 for 和 in 語句使用所必須的。 此方法對應於 Python/C API 中 Python 對象類型結構體的 tp_iter 槽位。spa

iterator.__next__()

從容器中返回下一項。 若是已經沒有項可返回,則會引起 StopIteration 異常。 此方法對應於 Python/C API 中 Python 對象類型結構體的 tp_iternext 槽位。code

Python 定義了幾種迭代器對象以支持對通常和特定序列類型、字典和其餘更特別的形式進行迭代。 除了迭代器協議的實現,特定類型的其餘性質對迭代操做來講都不重要。對象

一旦迭代器的 __next__() 方法引起了 StopIteration,它必須一直對後續調用引起一樣的異常。 不遵循此行爲特性的實現將沒法正常使用。排序

生成器類型

Python 的 generator 提供了一種實現迭代器協議的便捷方式。 若是容器對象 __iter__() 方法被實現爲一個生成器,它將自動返回一個迭代器對象(從技術上說是一個生成器對象),該對象提供 __iter__() 和 __next__() 方法。 有關生成器的更多信息能夠參閱 yield 表達式的文檔。索引

序列類型 --- list, tuple, range

有三種基本序列類型:list, tuple 和 range 對象。 爲處理 二進制數據 和 文本字符串 而特別定製的附加序列類型會在專門的小節中描述。接口

通用序列操做

大多數序列類型,包括可變類型和不可變類型都支持下表中的操做。 collections.abc.Sequence ABC 被提供用來更容易地在自定義序列類型上正確地實現這些操做。

此表按優先級升序列出了序列操做。 在表格中,s 和 t 是具備相同類型的序列,n, i, j 和 k 是整數,而 x 是任何知足 s 所規定的類型和值限制的任意對象。

in 和 not in 操做具備與比較操做相同的優先級。 + (拼接) 和 * (重複) 操做具備與對應數值運算相同的優先級。

clipboard.png

相同類型的序列也支持比較。 特別地,tuple 和 list 的比較是經過比較對應元素的字典順序。 這意味着想要比較結果相等,則每一個元素比較結果都必須相等,而且兩個序列長度必須相同。 (完整細節請參閱語言參考的 比較運算 部分。)

註釋:

  1. 雖然 in 和 not in 操做在一般狀況下僅被用於簡單的成員檢測,某些專門化序列 (例如 str, bytes 和 bytearray) 也使用它們進行子序列檢測:
>>>

    >>> "gg" in "eggs"
    True
  1. 小於 0 的 n 值會被看成 0 來處理 (生成一個與 s 同類型的空序列)。

請注意序列 s 中的項並不會被拷貝;它們會被屢次引用。 這一點常常會令 Python 編程新手感到困擾;例如:

>>>

    >>> lists = [[]] * 3
    >>> lists
    [[], [], []]
    >>> lists[0].append(3)
    >>> lists
    [[3], [3], [3]]

具體的緣由在於 [[]] 是一個包含了一個空列表的單元素列表,因此 [[]] * 3 結果中的三個元素都是對這一個空列表的引用。 修改 lists 中的任何一個元素實際上都是對這一個空列表的修改。 你能夠用如下方式建立以不一樣列表爲元素的列表:

>>>

    >>> lists = [[] for i in range(3)]
    >>> lists[0].append(3)
    >>> lists[1].append(5)
    >>> lists[2].append(7)
    >>> lists
    [[3], [5], [7]]

    進一步的解釋能夠在 FAQ 條目 如何建立多維列表? 中查看。
  1. 若是 i 或 j 爲負值,則索引順序是相對於序列 s 的末尾: 索引號會被替換爲 len(s) + i 或 len(s) + j。 但要注意 -0 仍然爲 0。
  2. s 從 i 到 j 的切片被定義爲全部知足 i <= k < j 的索引號 k 的項組成的序列。 若是 i 或 j 大於 len(s),則使用 len(s)。 若是 i 被省略或爲 None,則使用 0。 若是 j 被省略或爲 None,則使用 len(s)。 若是 i 大於等於 j,則切片爲空。
  3. s 從 i 到 j 步長爲 k 的切片被定義爲全部知足 0 <= n < (j-i)/k 的索引號 x = i + nk 的項組成的序列。 換句話說,索引號爲 i, i+k, i+2k, i+3*k,以此類推,當達到 j 時中止 (但必定不包括 j)。 當 k 爲正值時,i 和 j 會被減至不大於 len(s)。 當 k 爲負值時,i 和 j 會被減至不大於 len(s) - 1。 若是 i 或 j 被省略或爲 None,它們會成爲「終止」值 (是哪一端的終止值則取決於 k 的符號)。 請注意,k 不可爲零。 若是 k 爲 None,則看成 1 處理。
  4. 拼接不可變序列老是會生成新的對象。 這意味着經過重複拼接來構建序列的運行時開銷將會基於序列總長度的乘方。 想要得到線性的運行時開銷,你必須改用下列替代方案之一:
  • 若是拼接 str 對象,你能夠構建一個列表並在最後使用 str.join() 或是寫入一個 io.StringIO 實例並在結束時獲取它的值
  • 若是拼接 bytes 對象,你能夠相似地使用 bytes.join() 或 io.BytesIO,或者你也可使用 bytearray 對象進行原地拼接。 bytearray 對象是可變的,而且具備高效的重分配機制
  • 若是拼接 tuple 對象,請改成擴展 list 類
  • 對於其它類型,請查看相應的文檔
  1. 某些序列類型 (例如 range) 僅支持遵循特定模式的項序列,所以並不支持序列拼接或重複。
  2. 當 x 在 s 中找不到時 index 會引起 ValueError。 不是全部實現都支持傳入額外參數 i 和 j。 這兩個參數容許高效地搜索序列的子序列。 傳入這兩個額外參數大體至關於使用 s[i:j].index(x),可是不會複製任何數據,而且返回的索引是相對於序列的開頭而非切片的開頭。

不可變序列類型

不可變序列類型廣泛實現而可變序列類型未實現的惟一操做就是對 hash() 內置函數的支持。

這種支持容許不可變類型,例如 tuple 實例被用做 dict 鍵,以及存儲在 set 和 frozenset 實例中。

嘗試對包含有不可哈希值的不可變序列進行哈希運算將會致使 TypeError。

可變序列類型

如下表格中的操做是在可變序列類型上定義的。 collections.abc.MutableSequence ABC 被提供用來更容易地在自定義序列類型上正確實現這些操做。

表格中的 s 是可變序列類型的實例,t 是任意可迭代對象,而 x 是符合對 s 所規定類型與值限制的任何對象 (例如,bytearray 僅接受知足 0 <= x <= 255 值限制的整數)。

clipboard.png

註釋:

  1. t 必須與它所替換的切片具備相同的長度。
  2. 可選參數 i 默認爲 -1,所以在默認狀況下會移除並返回最後一項。
  3. 當在 s 中找不到 x 時 remove 操做會引起 ValueError。
  4. 當反轉大尺寸序列時 reverse() 方法會原地修改該序列以保證空間經濟性。 爲提醒用戶此操做是經過間接影響進行的,它並不會返回反轉後的序列。
  5. 包括 clear() 和 copy() 是爲了與不支持切片操做的可變容器 (例如 dict 和 set) 的接口保持一致

    3.3 新版功能: clear() 和 copy() 方法。

  6. n 值爲一個整數,或是一個實現了 __index__() 的對象。 n 值爲零或負數將清空序列。 序列中的項不會被拷貝;它們會被屢次引用,正如 通用序列操做 中有關 s * n 的說明。

列表

列表是可變序列,一般用於存放同類項目的集合(其中精確的類似程度將根據應用而變化)。

class list([iterable])

能夠用多種方式構建列表:
  1. 使用一對方括號來表示空列表: []
  2. 使用方括號,其中的項以逗號分隔: [a], [a, b, c]
  3. 使用列表推導式: [x for x in iterable]
  4. 使用類型的構造器: list() 或 list(iterable)

構造器將構造一個列表,其中的項與 iterable 中的項具備相同的的值與順序。 iterable 能夠是序列、支持迭代的容器或其它可迭代對象。 若是 iterable 已是一個列表,將建立並返回其副本,相似於 iterable[:]。 例如,list('abc') 返回 ['a', 'b', 'c'] 而 list( (1, 2, 3) ) 返回 [1, 2, 3]。 若是沒有給出參數,構造器將建立一個空列表 []。

其它許多操做也會產生列表,包括 sorted() 內置函數。

列表實現了全部 通常 和 可變 序列的操做。 列表還額外提供瞭如下方法:

#### sort(*, key=None, reverse=False)

此方法會對列表進行原地排序,只使用 < 來進行各項間比較。 異常不會被屏蔽 —— 若是有任何比較操做失敗,整個排序操做將失敗(而列表可能會處於被部分修改的狀態)。

sort() 接受兩個僅限以關鍵字形式傳入的參數 (僅限關鍵字參數):

key 指定帶有一個參數的函數,用於從每一個列表元素中提取比較鍵 (例如 key=str.lower)。 對應於列表中每一項的鍵會被計算一次,而後在整個排序過程當中使用。 默認值 None 表示直接對列表項排序而不計算一個單獨的鍵值。

可使用 functools.cmp_to_key() 將 2.x 風格的 cmp 函數轉換爲 key 函數。

reverse 爲一個布爾值。 若是設爲 True,則每一個列表元素將按反向順序比較進行排序。

當順序大尺寸序列時此方法會原地修改該序列以保證空間經濟性。 爲提醒用戶此操做是經過間接影響進行的,它並不會返回排序後的序列(請使用 sorted() 顯示地請求一個新的已排序列表實例)。

sort() 方法確保是穩定的。 若是一個排序確保不會改變比較結果相等的元素的相對順序就稱其爲穩定的 --- 這有利於進行多重排序(例如先按部門、再接薪級排序)。

CPython implementation detail: 在一個列表被排序期間,嘗試改變甚至進行檢測也會形成未定義的影響。 Python 的 C 實現會在排序期間將列表顯示爲空,若是發現列表在排序期間被改變將會引起 ValueError。

元組

元組是不可變序列,一般用於儲存異構數據的多項集(例如由 enumerate() 內置函數所產生的二元組)。 元組也被用於須要同構數據的不可變序列的狀況(例如容許存儲到 set 或 dict 的實例)。

class tuple([iterable])

能夠用多種方式構建元組:
  1. 使用一對圓括號來表示空元組: ()
  2. 使用一個後綴的逗號來表示單元組: a, 或 (a,)
  3. 使用以逗號分隔的多個項: a, b, c or (a, b, c)
  4. 使用內置的 tuple(): tuple() 或 tuple(iterable)

構造器將構造一個元組,其中的項與 iterable 中的項具備相同的值與順序。 iterable 能夠是序列、支持迭代的容器或其餘可迭代對象。 若是 iterable 已是一個元組,會不加改變地將其返回。 例如,tuple('abc') 返回 ('a', 'b', 'c') 而 tuple( [1, 2, 3] ) 返回 (1, 2, 3)。 若是沒有給出參數,構造器將建立一個空元組 ()。

請注意決定生成元組的實際上是逗號而不是圓括號。 圓括號只是可選的,生成空元組或須要避免語法歧義的狀況除外。 例如,f(a, b, c) 是在調用函數時附帶三個參數,而 f((a, b, c)) 則是在調用函數時附帶一個三元組。

元組實現了全部 通常 序列的操做。

對於經過名稱訪問相比經過索引訪問更清晰的異構數據多項集,collections.namedtuple() 多是比簡單元組對象更爲合適的選擇。

range 對象

range 類型表示不可變的數字序列,一般用於在 for 循環中循環指定的次數。

class range(stop)

class range(start, stop[, step])

range 構造器的參數必須爲整數(能夠是內置的 int 或任何實現了 __index__ 特殊方法的對象)。 若是省略 step 參數,其默認值爲 1。 若是省略 start 參數,其默認值爲 0,若是 step 爲零則會引起 ValueError。

若是 step 爲正值,肯定 range r 內容的公式爲 r[i] = start + step*i 其中 i >= 0 且 r[i] < stop。

若是 step 爲負值,肯定 range 內容的公式仍然爲 r[i] = start + step*i,但限制條件改成 i >= 0 且 r[i] > stop.

若是 r[0] 不符合值的限制條件,則該 range 對象爲空。 range 對象確實支持負索引,可是會將其解讀爲從正索引所肯定的序列的末尾開始索引。

元素絕對值大於 sys.maxsize 的 range 對象是被容許的,但某些特性 (例如 len()) 可能引起 OverflowError。

一些 range 對象的例子:
>>> list(range(10))
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    >>> list(range(1, 11))
    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    >>> list(range(0, 30, 5))
    [0, 5, 10, 15, 20, 25]
    >>> list(range(0, 10, 3))
    [0, 3, 6, 9]
    >>> list(range(0, -10, -1))
    [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
    >>> list(range(0))
    []
    >>> list(range(1, 0))
    []
range 對象實現了 通常 序列的全部操做,但拼接和重複除外(這是因爲 range 對象只能表示符合嚴格模式的序列,而重複和拼接一般都會違反這樣的模式)。

start

    start 形參的值 (若是該形參未提供則爲 0)

stop

    stop 形參的值

step

    step 形參的值 (若是該形參未提供則爲 1)

range 類型相比常規 list 或 tuple 的優點在於一個 range 對象老是佔用固定數量的(較小)內存,不論其所表示的範圍有多大(由於它只保存了 start, stop 和 step 值,並會根據須要計算具體單項或子範圍的值)。

range 對象實現了 collections.abc.Sequence ABC,提供如包含檢測、元素索引查找、切片等特性,並支持負索引 (參見 序列類型 --- list, tuple, range):

>>> r = range(0, 20, 2)
>>> r
range(0, 20, 2)
>>> 11 in r
False
>>> 10 in r
True
>>> r.index(10)
5
>>> r[5]
10
>>> r[:5]
range(0, 10, 2)
>>> r[-1]
18

使用 == 和 != 檢測 range 對象是否相等是將其做爲序列來比較。 也就是說,若是兩個 range 對象表示相同的值序列就認爲它們是相等的。 (請注意比較結果相等的兩個 range 對象可能會具備不一樣的 start, stop 和 step 屬性,例如 range(0) == range(2, 1, 3) 而 range(0, 3, 2) == range(0, 4, 2)。)

在 3.2 版更改: 實現 Sequence ABC。 支持切片和負數索引。 使用 int 對象在固定時間內進行成員檢測,而不是逐一迭代全部項。

在 3.3 版更改: 定義 '==' 和 '!=' 以根據 range 對象所定義的值序列來進行比較(而不是根據對象的標識)。

3.3 新版功能: start, stop 和 step 屬性。

參見

linspace recipe 演示瞭如何實現一個延遲求值版本的適合浮點數應用的 range 對象。
上一篇文章: Python標準庫---十、內置類型:數字類型
下一篇文章: Python標準庫---十二、內置類型:文本序列類型(str)
相關文章
相關標籤/搜索