1. 程式人生 > 實用技巧 >第6章-函式的高階應用-迭代器&生成器

第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 迭代器的工作原理

使用迭代器的步驟簡述如下:

  1. 呼叫內建函式iter(container), 把容器作為引數傳遞進去,返回一個物件,這個物件就是一個迭代器物件。容器物件就是咱們前邊說的str,list
  2. 迭代器物件中有一個方法__next()__,這個方法每呼叫一次,就可以訪問到容器中的一個元素,我們自己要呼叫的話,只需要呼叫內建函式next(it)
    就可以了
  3. 當容器中最後一個元素被迭代後, 再呼叫__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 生成器表示式和列表的差異

從寫法上來看,生成器表示式使用(), 而列表用[]. 但是他們之間還是有很重要的差異.

他們的主要區別在於其中的元素(資料)的生成時間不同!

  1. 列表建立成功之後, 那麼他裡面的元素也已經建立成功, 而且是實實在在的佔據著記憶體! 也就是說, 從物理上來看他們已經存在了.
  2. 而生成器表示式不一樣, 僅僅是建立了一個生成器而已, 那些元素還沒有建立. 只有當你使用for或者next()的時候才會根據需要來建立元素.
  3. 所以, 生成器不可能有新增, 刪除等這些方法.
  4. 如果資料量比較大的時候, 使用生成器表示式的效能要好於列表.
  5. 列表推倒只能生成列表, 而生成器表示式可以根據需要生成任何型別的序列.****