1. 程式人生 > >Python生成器、裝飾器

Python生成器、裝飾器

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         yield
number 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 result
8 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生成器、裝飾器