這裏咱們先來回顧一下什麼是可迭代對象(Iterable)?python
能夠直接做用於for循環的對象統稱爲可迭代對象,即Iterable。
# 一是集合數據類型,如list、tuple、dict、set、str等;
# 二是generator,包括生成器和帶yield的generator function。數據庫
那麼什麼又是迭代器(Iterator)?express
能夠被next()函數調用並不斷返回下一個值(直到沒有數據時拋出StopIteration錯誤)的對象稱爲迭代器,即Iterator。app
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。
首先先理清幾個概念:ide
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.oop
可見,咱們常說的生成器,
就是帶有 yield
的函數,而 generator iterator
則是 generator function
的返回值,即一個 generator
對象;網站
形如 (elem for elem in [1, 2, 3])
的表達式,稱爲 generator expression
,實際使用與 generator
無異。spa
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的。
什麼是裝飾器(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__)
如今是否是屬性也沒改了?