第6章-函式的高階應用-迭代器&生成器
python作為一個既面向物件,又支援函數語言程式設計的語言,函式的使用方面有很多特點。
比如:閉包,裝飾器,迭代器等
函式的高階應用
容器:生活中常見的容器有哪些?袋子,盆子,水杯,書包,鉛筆盒。。。
容器是一種把多個元素組織在一起的資料結構,容器中的元素可以逐個的迭代獲取,可以用in,not in等關鍵字判斷某個元素是否包含在容器中。在python中常見的容器物件有:
list
, tuple
, dict
, str
, set
容器你可以把它看做一個房子,一個櫃子,一個盒子,裡面可以塞任何東西,從技術角度來說,當他可以用來詢問某個元素是否包含在其中時,那麼這個物件就可以看做一個容器。
1 in [1, 2, 3] # True 4 not in [1, 2, 3, 4] # False 4 in {1, 2, 3} # False 4 not in {1, 2, 3, 4} # False
一、迭代器
回想一下,到目前為止,能用for迴圈進行遍歷的資料型別主要有哪些呢?dict,tuple,str,set,list
等
1.1 可迭代物件
可迭代物件和容器一樣是一種通俗的叫法,並不是指某種具體的資料型別,list
是可迭代物件,dict
是可迭代物件,set
也是可迭代物件。
這些可以直接作用於for
迴圈的物件統稱為可迭代物件:Iterable
。
前面說的序列和集合型別也是基於這個原理5
那麼,如何判斷一個物件是可迭代物件呢?方法是通過collections模組的Iterable型別判斷:
from collections import Iterable # str是否可迭代 print(isinstance("abc", Iterable)) # True # list是否可迭代 print(isinstance([1,2,3], Iterable)) # True # 整數是否可迭代 print(isinstance(123, Iterable)) # False
1.2 迭代器
現在已經有很多物件可以使用for
迴圈,而且相對於其他語言,pythonfor
迴圈的使用方式非常優雅,簡潔和便利
for
迴圈的背後就是迭代器
迭代器使用遍及python,並且使用方式統一
但凡是返回一個迭代器的物件,都可以成為可迭代物件。比如range
物件。
1.3 迭代器的工作原理
使用迭代器的步驟簡述如下:
- 呼叫內建函式
iter(container)
, 把容器作為引數傳遞進去,返回一個物件,這個物件就是一個迭代器物件。容器物件就是咱們前邊說的str,list
等- 迭代器物件中有一個方法
__next()__
,這個方法每呼叫一次,就可以訪問到容器中的一個元素,我們自己要呼叫的話,只需要呼叫內建函式next(it)
就可以了- 當容器中最後一個元素被迭代後, 再呼叫
__next()__
方法, 則會丟擲一個StopIteration
異常,for
迴圈捕捉到這個異常後就可以終止迴圈了.
當執行程式碼:
list1 = [1, 2, 3]
for x in list1:
....
實際執行的情況是
1.3 使用迭代器訪問字串中的元素
s = "acdefgh"
# 獲取字串 s 的迭代器, 其實等價於 it = s.__iter__()
it = iter(s)
print(next(it))# 迭代第一個元素
print(next(it))
print(next(it))
print(next(it))
print(next(it))
1.4 使用迭代器訪問列表中的元素
s = [10, 30, 40, 20, 2]
it = iter(s)
print(next(it)) # 10
print(next(it)) # 30
print(next(it)) # 40
二、生成器
生成器算的上是python語言中最吸引人的特性之一,生成器其實是一種特殊的迭代器,不過這種迭代器更加優雅。他只需要一個yiled關鍵字。生成器一定是迭代器(反之不成立),因此任何生成器也是以一種懶載入的模式生成值。
generator
(生成器)是一個簡單且強大的建立迭代器的工具
生成器除了使用yield
之外, 就像一個正常的函式, 想在任何地方返回資料, 只需要新增yield
就可以了.
我們呼叫next()
函式, 一旦碰到yield
則返回yield
後的資料, python 並且可以儲存當前的狀態和位置, 下次再呼叫next()
, 則繼續從此處執行.
# 生成能夠迭代整數 1-n 的迭代器函式. 呼叫這個方法, 方法內的程式碼並不會立即執行, 而是返回一個生成器物件
def foo(n):
for i in range(1, n):
yield i # 每次碰到 yield, 則在此暫停, 並儲存這個位置
for i in foo(20):
print(i)
當然也可以使用下面的方式去使用生成器函式
def foo(n):
for i in range(1, n):
yield i # 每次碰到 yield, 則在此暫停, 並儲存這個位置
it = foo(20)
print(next(it))
print(next(it))
1.2.1 生成器表示式
生成器表示式(Generator Expressions), 是一個物件, 他執行的結果和以前學習的列表推導類似, 但會迭代的生成結果.
他的語法也與列表類似, 只是需要把以前的[]
換成()
語法:
it = (express for item in iterator)
def foo(n):
for i in range(1, n + 1):
yield i
# foo(20)返回的迭代器生成一個新的迭代器
it = (x * x for x in foo(20))
for y in it:
print(y)
1.2.2 生成器表示式和列表的差異
從寫法上來看,生成器表示式使用()
, 而列表用[]
. 但是他們之間還是有很重要的差異.
他們的主要區別在於其中的元素(資料)的生成時間不同!
- 列表建立成功之後, 那麼他裡面的元素也已經建立成功, 而且是實實在在的佔據著記憶體! 也就是說, 從物理上來看他們已經存在了.
- 而生成器表示式不一樣, 僅僅是建立了一個生成器而已, 那些元素還沒有建立. 只有當你使用
for
或者next()
的時候才會根據需要來建立元素. - 所以, 生成器不可能有新增, 刪除等這些方法.
- 如果資料量比較大的時候, 使用生成器表示式的效能要好於列表.
- 列表推倒只能生成列表, 而生成器表示式可以根據需要生成任何型別的序列.****