python迭代器、生成器、裝飾器


1 迭代器

這里我們先來回顧一下什么是可迭代對象(Iterable)

可以直接作用於for循環的對象統稱為可迭代對象,即Iterable。
  # 一是集合數據類型,如list、tuple、dict、set、str等;
  # 二是generator,包括生成器和帶yield的generator function。

那么什么又是迭代器(Iterator)?

可以被next()函數調用不斷返回下一個值(直到沒有數據時拋出StopIteration錯誤)的對象稱為迭代器,即Iterator。

1 import collections
2 print(isinstance([], collections.Iterable))            # True
3 print(isinstance(iter([]), collections.Iterator))      # True
4 print(isinstance(iter([]), collections.Iterable))      # True
5 print(isinstance([], collections.Iterator))            # False
6 print(isinstance((x * x for x in range(10)), collections.Iterable))
# isinstance() 是python內建函數,返回對象是否是類或其子類的實例。若是,返回True,反之返回False。
# Iterable 英文是‘可迭代的’,形容詞;Iterator英文是‘迭代器’,名詞。

# 那么當 isinstance()的第二個參數是collections.Iterable時,是判斷第一個參數是不是Iterable對象(可迭代對象)
# 當 isinstance()的第二個參數是collections.Iterator時,是判斷第一個參數是不是Iterator對象(迭代器對象)

# 那么什么是可迭代對象?可以直接作用於for循環的對象統稱為可迭代對象,即Iterable。
# 一是集合數據類型,如list、tuple、dict、set、str等;
# 二是generator,包括生成器和帶yield的generator function。

# 是么是迭代器?可以被next()函數調用並不斷返回下一個值(直到沒有數據時拋出StopIteration錯誤)的對象稱為迭代器:Iterator。

# 你可能會問,為什么list、dict、str等數據類型不是Iterator?
# 這是因為Python的Iterator對象表示的是一個數據流,Iterator對象可以被next()函數調用並不斷返回下一個數據,直到沒有數據時拋出StopIteration錯誤。
# 可以把這個數據流看做是一個有序序列,但我們卻不能提前知道序列的長度,只能不斷通過next()函數實現按需計算下一個數據,
# 所以Iterator的計算是惰性的,只有在需要返回下一個數據時它才會計算。
# Iterable 可以通過iter()函數轉換得到 Iterator,但Iterable不一定是Iterator;而Iterator可以直接作用於for循環,所以Iterator是Iterable。

2 生成器

首先先理清幾個概念:

  generator : A function which returns a generator iterator. It looks like a normal function except that it contains yield expressions for producing a series of values usable in a for-loop or that can be retrieved one at a time with the next() function.

  generator iterator : An object created by a generator funcion.

  generator expression : An expression that returns an iterator.

      可見,我們常說的生成器,就是帶有 yield 的函數,而 generator iterator 則是 generator function 的返回值,即一個 generator 對象;

      形如 (elem for elem in [1, 2, 3]) 的表達式,稱為 generator expression ,實際使用與 generator 無異。

  Python’s generators provide a convenient way to implement the iterator protocol.

  也就是說: generator 就是 iterator 的一種,以更優雅的方式實現的 iterator 。我們來一個例子:

 1 from collections import Iterable
 2 from collections import Iterator
 3 from collections import Generator
 4 
 5 def odd():
 6     print('step 1')
 7     yield 1
 8     print('step 2')
 9     yield(3)
10     print('step 3')
11     yield(5)
12 
13 ge = odd()
14 print(isinstance(ge, Iterator))
15 print(isinstance(ge, Iterable))
16 print(isinstance(ge, Generator))
17 print(type(ge))
18 # 結果
19 # True
20 # True
21 # True
22 # <class 'generator'>

這也充分印證了上面的說法,generator就是一種iterator。而且Gennerator這個類是繼承了Iterator的。

 

3 裝飾器

什么是裝飾器(Decorator)?

  本質上:是一個返回函數的高階函數

生產上,什么時候用裝飾器?

  當我們想要給一個函數func()增加某些功能,但又不希望修改func()函數的源代碼的時候就需要用裝飾器了。(在代碼運行期間動態增加功能)

假如,你有一個網站,之前是免費開放的,誰都可以訪問。但是有一天你不想免費開放了,你想讓大家必須登陸后才能訪問,但是呢,網站已經上線了,一直是跑着的,不能修改源碼。這個時候就要用這個裝飾器了。

前奏:

假設你原先的網站首頁是這個函數:

def home():
    print("歡迎來到XX首頁!")

home()

首先我們必須得明白:函數也是一個對象(python里一切皆對象),且可以賦值給其他變量。例如:

def home():
    print("歡迎來到XX首頁!")

f = home
f()

這和直接調用home()結果是一樣的。

那么怎么做到,不改變home的源碼給它加上添加登錄功能呢?看下面的代碼,注意其中的講解:

 1 def login(func):
 2     """
 3     在這里從新定義一個高階函數,
 4     這就是decorator。
 5     我們一會兒會仔細分析。
 6     """
 7     def wrapper(*args, **kwargs):
 8         user = "zingp"   # 假設這是數據庫中的用戶名和密碼
 9         passwd = "123"
10         username = input("輸入用戶名:")
11         password = input("輸入密碼:")
12         if username == user and password == passwd:
13             return func(*args, **kwargs)
14         else:
15             print("用戶名或密碼錯誤。")
16     return wrapper
17 
18 
19 @login     # 利用python的@語法,把decorator置於home函數的定義處 相當於home = login(home)
20 def home():
21     print("歡迎來到XX首頁!")
22 
23 home()

運行看看,是不是沒改變home源碼和home的調用方式,給home添加了登錄驗證的功能?

其實,這里@login相當於做一這么一件事:home = login(home)

那么當執行23行時就是這樣的:login(home)()

  login(home)是什么?他就是調用login這個函數后的返回值,即wrapper

      此時,login(home)()即變成了 wrapper()

  執行wrapper() ,返回home()函數並執行home()

整個過程就是這樣。

 但是這里還有個問題,就是當沒加裝飾器的時候print(home.__name__)得到的函數名是home,加了裝飾器后print(home.__name__)得到的結果就是wrapper了。

我們雖然沒有改home函數的源代碼,但是更改了__name__屬性,所以,最美好的結果是連屬性也不更改吧?

你可能想寫個wrapper.__name__ = func.__name__這樣的代碼,就可以了嘛。但是呢,Python內置的functools.wraps就是專門干這個事的。

請看完整的裝飾器代碼

 1 import functools  # 先得導入這個工具
 2 
 3 
 4 def login(func):
 5 
 6     @functools.wraps(func)
 7     def wrapper(*args, **kw):
 8         user = "zingp"   # 假設這是數據庫中的用戶名和密碼
 9         passwd = "123"
10         username = input("輸入用戶名:")
11         password = input("輸入密碼:")
12         if username == user and password == passwd:
13             return func(*args, **kw)
14         else:
15             print("用戶名或密碼錯誤。")
16     return wrapper
17 
18 
19 @login
20 def home():
21     print("歡迎來到XX首頁!")
22 
23 home()
24 print(home.__name__)

現在是不是屬性也沒改了?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM