Python生成器、裝飾器
阿新 • • 發佈:2018-07-01
gen col BE for 原函數 RM pan rdquo _屬性 - 裝飾器的本質上是接受函數為參數的高階函數,它把一個函數作為參數輸入並且返回一個功能拓展後的新函數
## 生成器
- 生成器是用來創建Python序列的一個對象
- 通常生成器是為叠代器產生數據的
- 例如range()函數就是一個生成器
- 每次叠代生成器時,它都會記錄上一次調用的位置,並返回下一個值,這使程序不需要創建和存儲完整的序列
## 生成器函數
- 生成器函數與普通函數類似,但它的返回值使用yield語句,而不是return
1 def my_range(start=0, last=10, step=1): 2 number = start 3 while number < last: 4 yieldnumber 5 number += step 6 7 my_range # 是一個普通函數 8 # <function my_range at 0x7efe3dbf2e18> 9 10 my_range() # 返回一個生成器對象 11 # <generator object my_range at 0x7efe3daac360> 12 13 list(my_range(1, 10)) 14 # [1, 2, 3, 4, 5, 6, 7, 8, 9]
## 裝飾器
- 裝飾器的作用在於在不改變原有代碼結構的前提下,對原有代碼的功能進行補充擴展
1 # 裝飾器函數,為函數添加兩條語句 2 def deco(fn): 3 def new_func(*args): # 內部函數的參數需要與傳入的fn的參數相同 4 print("執行函數:{0}".format(fn.__name__)) 5 result = fn(*args) 6 print("函數執行結果:{0}".format(result)) 7 return result8 return new_func 9 10 11 @deco # 使用@裝飾函數名,使用裝飾器之後,add實際上已經指向了doco函數返回的新函數 12 def add(*args): 13 print("我是核心代碼,可不能改動我") 14 result = 0 15 for n in args: 16 result += n 17 return result 18 19 20 add(1, 2, 3, 4) 21 """ 22 執行結果: 23 執行函數:add 24 我是核心代碼,可不能改動我 25 函數執行結果:10 26 """
- 一個函數可以有多個裝飾器
- 最靠近函數的裝飾器會先執行,然後一次向上執行裝飾器
1 def count_param(fn): 2 def new_func(*args): 3 amount = len(args) 4 fn(*args) 5 print("參數個數為:{0}".format(amount)) 6 return amount 7 return new_func 8 9 10 @count_param 11 @deco 12 def add(*args): 13 print("我是核心代碼,可不能改動我") 14 result = 0 15 for n in args: 16 result += n 17 return result 18 19 20 add(1, 2, 3, 4) 21 """ 22 執行結果: 23 執行函數:add 24 我是核心代碼,可不能改動我 25 函數執行結果:10 26 參數個數為:4 27 """
- 如果decorator本身需要傳入參數,那就需要編寫一個返回decorator的高階函數
1 import time 2 3 4 def log(now_time): 5 def deco(fn): 6 def new_func(*args, **kwargs): 7 print(now_time) 8 return fn(*args, **kwargs) 9 return new_func 10 return deco 11 12 13 @log(time.asctime(time.localtime(time.time()))) 14 def add(*args): 15 print("我是核心代碼,可不能改動我") 16 result = 0 17 for n in args: 18 result += n 19 return result 20 21 22 add(1, 2, 3, 4) 23 """ 24 執行結果: 25 函數開始時間:Sun Jul 1 15:30:14 2018 26 我是核心代碼,可不能改動我 27 """
- 此時打印add函數的__name__屬性發現:
print("核心函數名:{0}".format(add.__name__)) """ 輸出: 核心函數名:new_func """
- 這表明雖然裝飾器表面上並沒有改變核心函數的內容,但實際上還是對核心函數的屬性進行了修改,所以還需要將核心函數的__name__屬性復制到新函數
1 import time 2 3 4 def log(now_time): 5 def deco(fn): 6 def new_func(*args, **kwargs): 7 # 將原函數的__name__屬性復制到新函數 8 new_func.__name__ = fn.__name__ 9 print(now_time) 10 return fn(*args, **kwargs) 11 return new_func 12 return deco 13 14 15 @log(time.asctime(time.localtime(time.time()))) 16 def add(*args): 17 print("我是核心代碼,可不能改動我") 18 result = 0 19 for n in args: 20 result += n 21 return result 22 23 24 add(1, 2, 3, 4) 25 print("核心函數名:{0}".format(add.__name__)) 26 """ 27 執行結果: 28 Sun Jul 1 15:43:00 2018 29 我是核心代碼,可不能改動我 30 核心函數名:add 31 """
- 在functools裏面有一個專門的函數處理這個問題
1 import time 2 import functools 3 4 5 def log(now_time): 6 def deco(fn): 7 @functools.wraps(fn) # 在新的函數上添加裝飾器,修改新函數的__name__屬性 8 def new_func(*args, **kwargs): 9 print(now_time) 10 return fn(*args, **kwargs) 11 return new_func 12 return deco 13 14 15 @log(time.asctime(time.localtime(time.time()))) 16 def add(*args): 17 print("我是核心代碼,可不能改動我") 18 result = 0 19 for n in args: 20 result += n 21 return result 22 23 24 add(1, 2, 3, 4) 25 print("核心函數名:{0}".format(add.__name__)) 26 ”“” 27 執行結果: 28 Sun Jul 1 15:48:10 2018 29 我是核心代碼,可不能改動我 30 核心函數名:add 31 “”“
本文參考:
[美]Bill Lubanovic 《Python語言及其應用》
https://www.liaoxuefeng.com 廖雪峰的官方網站
Python生成器、裝飾器