天天看點

[python] 三大神器—生成器、疊代器、裝飾器

文章目錄

      • 1. 生成器
        • 1.1 生成器表達式 (...)
        • 1.2 生成器函數 yield
        • 1.3 調用生成器 next() / send()
      • 2. 疊代器
        • 2.1 可疊代對象 - 可以for循環
          • 1.可用 `isinstance( .., Iterable)` 判斷可疊代對象
          • 2.疊代器還能被 `next()` 函數調用
        • 2.2 疊代器 - 可被 next() 調用
          • 1.可用 `isinstance(... , Iterator)` 判斷
          • 2.可以通過 `iter(Iterable)` 建立疊代器
          • 3.python的`for`循環本質
          • 4.range()不是疊代器,xrange()是
        • 2.3 生成器是一種特殊的疊代器
      • 3. 裝飾器
        • 3.1 實作需求!!!!!
        • 3.2 相關知識
          • 1. 函數是一個對象
          • 2. 高階函數(函數作為參數/傳回值)
          • 3. 嵌套函數
          • 4. 閉包(延伸了作用域的函數)
          • 5. 關于閉包的變量(nonlocal)
        • 3.3 裝飾器
          • 1. 裝飾器:高階函數 + 嵌套函數 + 閉包
          • 2. 文法糖 @
          • 3. 進階裝飾器 - 有參
          • 4. 一些讨論
[python] 三大神器—生成器、疊代器、裝飾器

1. 生成器

squares = [i**2 for i in range(10000)]
for i in squares:
	pass
           

把所有元素都存在清單裡,占用了大量記憶體,而實際上每次隻用到清單中的一個元素。

生成器:

1.惰性計算

2.邊執行邊計算,無需一次性存入大量資料

3.實際上一直在執行next(操作),直到無值可取

1.1 生成器表達式 (…)

圓括号

squares = (i**2 for i in range(100000))
for i in squares:
    pass
           

【例】求0-500的和

sum((i for i in range(501)))
## 5050
           

1.2 生成器函數 yield

【例】 生成斐波那契數列

1.數組疊代和存儲

[python] 三大神器—生成器、疊代器、裝飾器

2.若有中間操作,每次隻用到其中一個元素,比如每次隻列印一個數字(不需要一次性使用全部元素),可以把這個中間操作放在循環裡面執行

[python] 三大神器—生成器、疊代器、裝飾器

3.這種情況也可以構造生成器函數,友善單獨進行中間操作,把print換成yield即可

當我們調用這個函數fib(),它首先會順序執行,當執行到yield時停止,yield就會傳回yield後面跟着的那個元素,相當于 return 的功能,所不同的是,執行完傳回之後仍然會停在這裡,直到我們執行下一次的操作 next(),繼續執行剩餘操作,并傳回到while判斷語句,符合條件則執行 yield,再次傳回,并停留在這裡,等待下一次操作 next()…

總結:在每次調用 next() 的時候執行,遇到 yield 語句則傳回(相應元素),再次執行時從上次傳回的 yield 語句處繼續執行

[python] 三大神器—生成器、疊代器、裝飾器

1.3 調用生成器 next() / send()

[作業系統] 多任務 - 協程

2. 疊代器

2.1 可疊代對象 - 可以for循環

可直接作用于for循環的對象,統稱為可疊代對象:

Iterable

  • 清單,元組,字典,集合,字元串,檔案
  • 生成器
1.可用

isinstance( .., Iterable)

判斷可疊代對象
from collections import Iterable
isinstance([1,2,3], Iterable)
# True
isinstance("python", Iterable)
# True
           
2.疊代器還能被

next()

函數調用

比如生成器不僅可用于

for

循環,還可以被

next()

函數調用,直到沒有資料可取,抛出

StopIteration

(生成器不僅是可疊代對象,還是疊代器)

squares = (i**2 for i in range(5))
isinstance(squares, Iterable)
# True

print(next(squares))
print(next(squares))
print(next(squares))
0
1
4
           

2.2 疊代器 - 可被 next() 調用

可以被

next()

函數調用,并且不斷傳回下一個值,直到沒有資料可取的對象(可耗盡),稱為疊代器

Iterator

  • 生成器 是 Iterable 也是 Iterator
  • 清單,元組,字典,集合,字元串 是 Iterable 不是 Iterator
  • 檔案 是 Iterable 也是 Iterator
  • zip, enumerate 等 itertools 裡的函數是 Iterator
  • range() 不是 Iterator
1.可用

isinstance(... , Iterator)

判斷
from collections import Iterator

squares = (i**2 for i in range(5))  ## 生成器
isinstance(squares, Iterator)
# true

with open("test.txt", "r", encoding = "utf-8") as f:
    print(isinstance(f, Iterator))    ## 檔案
# True

isinstance([1,2], Iterator)   ## 清單
# False
isinstance("python", Iterator)  ## 字元串
# False
isinstance(range(5), Iterator) ## range函數
# False
           
x = [1, 2]
y = ["a", "b"]
for i in zip(x, y):    ## zip函數
    print(i)
# (1, 'a')
# (2, 'b')
isinstance(zip(x, y), Iterator)
# True
           
num = [1, 2, 3]
for i in enumerate(num): # enumerate函數
    print(i)
# (0, 1)
# (1, 2)
# (2, 3)
isinatance(enumerate(num), Iterator)
# True
           
2.可以通過

iter(Iterable)

建立疊代器

清單,元組,字典,集合,字元串 是 Iterable 不是 Iterator,但可以用 iter 建立 Iterator

isinstance(iter([1,2,3]), Iterator)
# True
           

PS:Iterator表示的是一個資料流,不斷被next()調用并傳回下一個資料直到StopIteration異常。但,我們不能提前知道序列長度,隻能通過 next() 按需計算下一個資料,(惰性計算),而清單等類型可以計算序列長度。Iterator甚至可以表示一個無限大的資料流,但清單等顯然不可能做到。

3.python的

for

循環本質

結合上一條,

for item in Iterable

實際上先通過

iter(Iterable )

函數建立可疊代對象的疊代器

Iterator

,然後對疊代器不斷調用

next()

方法來擷取下一個值,并将其指派給

item

,當遇到

StopIteration

的異常後 結束循環。

4.range()不是疊代器,xrange()是
nums = range(10)
isinstance(nums, Iterator)
# False
next(nums)        
# TypeError         # 不可被next()調用
           

【python 2】

輸入 range(100) 則傳回一個清單 [0, 1, 2, … ,99],如果輸入 range(100000…000) 那麼首先需要占用大量的記憶體空間來存儲這個清單結果,并且在清單的生成過程中就卡死了

輸入 xrange(100) 則傳回一個對象(疊代器),占用極小的記憶體空間,存儲的不是清單結果,而是生成清單的方式

【python 3 】已經把 range 改為了 xrange

2.3 生成器是一種特殊的疊代器

疊代:通路集合元素的一種方式(在原來的基礎上,得到新的元素)

疊代器:一個對象,可以用很小的記憶體資源來通路集合元素(通過調用 next() 方法,疊代擷取資料),比如 for temp in Iterable , [ xxx for xxx in iterable] 存儲的不是清單結果,而是生成清單的方式。

生成器:一個函數,傳回的是疊代器,能夠記錄目前疊代到的狀态,并配合 next() 函數進行疊代,可以生成資料。

特殊在于,疊代器必須是一個類裡有 iter 方法和 next() 方法,它的對象才能是一個疊代器,但是生成器不需要,隻要一個函數裡有 yield,那麼這個函數就是生成器,可以通過 next() 函數來調用這個生成器,進而生成資料,每調用一次隻生成一個值,然後暫停執行,直到下次調用。

[作業系統] 多任務 - 協程

3. 裝飾器

3.1 實作需求!!!!!

【1】需要對已開發上線的程式添加某些功能

【2】不能對程式中函數的源代碼進行修改

【3】不能改變程式中函數的調用方式

裝飾器,就是對原函數進行【裝飾】

3.2 相關知識

1. 函數是一個對象

可以将函數指派給變量,調用變量就是調用函數

def square(x):
    return x**2
print(type(square))  # square 是 function 類的一個執行個體
# <class 'function'>

pow_2 = square   # 相當于給square函數起了别名
print(pow_2(5))
print(square(5))
# 25
# 25
           
2. 高階函數(函數作為參數/傳回值)

接收函數作為參數,或者傳回函數作為傳回值,滿足之一稱為高階函數

def square(x):
    return x**2
def pow_2(fun):
    return fun
f = pow_2(square)   ### Not pow_2(square(8))
f(8)
# 64
           
3. 嵌套函數

在函數内部又定義一個函數

def outer():
    print("outer is running")
    
    def inner():
        print("inner is running")
    inner()

outer()
# outer is running
# inner is running
           
4. 閉包(延伸了作用域的函數)

閉包是延伸了作用域的函數

閉包 = 函數+外層環境的變量

如果一個函數定義在另一個函數的作用域内,并且引用了外層函數的變量,則該函數稱為閉包

[python] 三大神器—生成器、疊代器、裝飾器
5. 關于閉包的變量(nonlocal)

一旦在内層函數重新定義了相同名字的變量,則該變量成為局部變量,外層函數對其的定義和初始化全部失效(例子裡就是 x = x +100 的 x 變成局部變量了,無初始值,報錯)

[python] 三大神器—生成器、疊代器、裝飾器

可以使用

nonlocal

允許内層函數修改閉包變量,且不會變成内部變量

global

的作用對象是全局變量(聲明代碼塊中的變量使用外部全局的同名變量),

nonlocal

的作用對象是外層變量(使用在閉包中的,聲明内層函數使用外層的同名變量)

[python] 三大神器—生成器、疊代器、裝飾器

3.3 裝飾器

1. 裝飾器:高階函數 + 嵌套函數 + 閉包

個人了解:

裝飾器函數是個【嵌套函數】,新增的功能由内層函數實作。

裝飾器函數也是個【高階函數】,外層函數把原函數作為參數傳入(内層函數執行時用到),外層函數的傳回值是内層函數,注意傳回值是 inner,不是 inner()

裝飾器的内層函數是個【閉包】,執行時用到了外層的參數(原函數)

[python] 三大神器—生成器、疊代器、裝飾器
2. 文法糖 @
[python] 三大神器—生成器、疊代器、裝飾器
3. 進階裝飾器 - 有參

【函數有參】可以在裝飾器的内層函數引入參數(保險起見,兩種參數都寫上),把外層函數的參數傳遞給内層函數

def timer(func):
    def inner(*args, **kwargs):
        ...
        func(*args, **kwargs)
        ...
@timer
def f1(n):
    ...

f1(2) 
           
[python] 三大神器—生成器、疊代器、裝飾器

【函數有傳回值】可以在裝飾器的内層函數調用 原函數 的時候,将結果賦給一個變量比如 res,将 res 作為記憶體函數的傳回值

[python] 三大神器—生成器、疊代器、裝飾器

【裝飾器有參】裝飾器本身需要一些參數,比如針對不同的函數進行裝飾,比如針對不同的需求進行裝飾,需要傳遞一些選擇分支性質的參數

[python] 三大神器—生成器、疊代器、裝飾器
4. 一些讨論
  1. 原函數的屬性被掩蓋了,傳回的是裝飾器函數的内層函數:f1 = timer(f1),是以 f1 = inner
  2. 可以使用 @wraps 實作回歸本源
    [python] 三大神器—生成器、疊代器、裝飾器