Python入門篇-裝飾器

              Python入門篇-裝飾器
python

                                      做者:尹正傑app

版權聲明:原創做品,謝絕轉載!不然將追究法律責任。ide

 

 

一.裝飾器概述函數

裝飾器(無參)
  它是一個函數
  函數做爲它的形參
  返回值也是一個函數
  能夠使用@functionname方式,簡化調用

裝飾器和高階函數
  裝飾器是高階函數,但裝飾器是對傳入函數的功能的裝飾(功能加強)

帶參裝飾器
  它是一個函數
  函數做爲它的形參
  返回值是一個不帶參的裝飾器函數
  使用@functionname(參數列表)方式調用
  能夠看作在裝飾器外層又加了一層函數

 

二.爲何要用裝飾器spa

1>.在不是用裝飾器的狀況下,給某個函數添加功能日誌

在解釋爲何使用裝飾器以前,完美來看一個需求:
  一個加法函數,想加強它的功能,可以輸出被調用過以及調用的參數信息     
def add(x, y):       return x + y
  增長信息輸出功能:     
def add(x, y):       print("call add, x + y") # 日誌輸出到控制檯       return x + y
  上面的加法函數是完成了需求,可是有如下的缺點     打印語句的耦合過高,換句話說,咱們不推薦去修改初始的add函數原始代碼。     加法函數屬於業務功能,而輸出信息的功能,屬於非業務功能代碼,不應放在業務函數加法中

2>.使用高階函數給某個函數添加功能code

 1 #!/usr/bin/env python
 2 #_*_coding:utf-8_*_
 3 #@author :yinzhengjie
 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
 5 #EMAIL:y1053419035@qq.com
 6 
 7 
 8 def add(x,y):
 9     return x + y
10 
11 def logger(func):
12     print('begin') # 加強的輸出
13     f = func(4,5)
14     print('end') # 加強的功能
15     return f
16 
17 print(logger(add))
18 
19 
20 
21 #以上代碼輸出結果以下:
22 begin
23 end
24 9

3>.解決了傳參的問題,進一步改變orm

 1 #!/usr/bin/env python
 2 #_*_coding:utf-8_*_
 3 #@author :yinzhengjie
 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
 5 #EMAIL:y1053419035@qq.com
 6 
 7 
 8 def add(x,y):
 9     return x + y
10 
11 def logger(func,*args,**kwargs):
12     print('begin') # 加強的輸出
13     f = func(*args,**kwargs)
14     print('end') # 加強的功能
15     return f
16 
17 print(logger(add,5,y=60))
18 
19 
20 
21 #以上代碼輸出結果以下:
22 begin
23 end
24 65

4>.柯里化實現add函數功能加強對象

 1 #!/usr/bin/env python
 2 #_*_coding:utf-8_*_
 3 #@author :yinzhengjie
 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
 5 #EMAIL:y1053419035@qq.com
 6 
 7 def add(x,y):
 8     return x + y
 9 
10 def logger(fn):
11     def wrapper(*args,**kwargs):
12         print('begin')
13         x = fn(*args,**kwargs)
14         print('end')
15         return x
16     return wrapper
17 
18 # print(logger(add)(5,y=50))        #海航代碼等價於下面兩行代碼,只是換了一種寫法而已
19 add = logger(add)
20 print(add(x=5, y=10))
21 
22 
23 #以上代碼輸出結果以下:
24 begin
25 end
26 15

5>.裝飾器語法糖blog

#!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com


"""
    定義一個裝飾器
"""
def logger(fn):
    def wrapper(*args,**kwargs):
        print('begin')
        x = fn(*args,**kwargs)
        print('end')
        return x
    return wrapper

@logger # 等價於add = logger(add),這就是裝飾器語法
def add(x,y):
    return x + y

print(add(45,40))



#以上代碼輸出結果以下:
begin
end
85

 

三.幫助文檔之文檔字符串

1>.定義python的文檔字符串

 1 #!/usr/bin/env python
 2 #_*_coding:utf-8_*_
 3 #@author :yinzhengjie
 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
 5 #EMAIL:y1053419035@qq.com
 6 
 7 
 8 """
 9 Python的文檔
10     Python是文檔字符串Documentation Strings
11     在函數語句塊的第一行,且習慣是多行的文本,因此多使用三引號
12     慣例是首字母大寫,第一行寫概述,空一行,第三行寫詳細描述
13     能夠使用特殊屬性__doc__訪問這個文檔
14 """
15 
16 def add(x,y):
17     """This is a function of addition"""
18     a = x+y
19     return x + y
20 
21 print("name = {}\ndoc = {}".format(add.__name__, add.__doc__))
22 
23 print(help(add))
24 
25 
26 
27 #以上代碼執行結果以下:
28 name = add
29 doc = This is a function of addition
30 Help on function add in module __main__:
31 
32 add(x, y)
33     This is a function of addition
34 
35 None

2>.裝飾器的反作用

 1 #!/usr/bin/env python
 2 #_*_coding:utf-8_*_
 3 #@author :yinzhengjie
 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
 5 #EMAIL:y1053419035@qq.com
 6 
 7 
 8 def logger(fn):
 9     def wrapper(*args,**kwargs):
10         'I am wrapper'
11         print('begin')
12         x = fn(*args,**kwargs)
13         print('end')
14         return x
15     return wrapper
16 
17 @logger #add = logger(add)
18 def add(x,y):
19     '''This is a function for add'''
20     return x + y
21 
22 
23 print("name = {}\ndoc= {}".format(add.__name__, add.__doc__))      #原函數對象的屬性都被替換了,而使用裝飾器,咱們的需求是查看被封裝函數的屬性,如何解決?
24 
25 
26 
27 
28 #以上代碼執行結果以下:
29 name = wrapper
30 doc= I am wrapper

3>.提供一個函數,被封裝函數屬性==copy==> 包裝函數屬性

 1 #!/usr/bin/env python
 2 #_*_coding:utf-8_*_
 3 #@author :yinzhengjie
 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
 5 #EMAIL:y1053419035@qq.com
 6 
 7 
 8 """
 9     經過copy_properties函數將被包裝函數的屬性覆蓋掉包裝函數
10     凡是被裝飾的函數都須要複製這些屬性,這個函數很通用
11     能夠將複製屬性的函數構建成裝飾器函數,帶參裝飾器
12 """
13 def copy_properties(src, dst): # 能夠改形成裝飾器
14     dst.__name__ = src.__name__
15     dst.__doc__ = src.__doc__
16 
17 def logger(fn):
18     def wrapper(*args,**kwargs):
19         'I am wrapper'
20         print('begin')
21         x = fn(*args,**kwargs)
22         print('end')
23         return x
24     copy_properties(fn, wrapper)
25     return wrapper
26 
27 @logger #add = logger(add)
28 def add(x,y):
29     '''This is a function for add'''
30     return x + y
31 
32 print("name = {}\ndoc = {}".format(add.__name__, add.__doc__))
33 
34 
35 
36 
37 #以上代碼執行結果以下:
38 name = add
39 doc = This is a function for add

4>.提供一個函數,被封裝函數屬性==copy==> 包裝函數屬性,改形成帶參裝飾器

 1 #!/usr/bin/env python
 2 #_*_coding:utf-8_*_
 3 #@author :yinzhengjie
 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
 5 #EMAIL:y1053419035@qq.com
 6 
 7 
 8 def copy_properties(src): # 柯里化
 9     def _copy(dst):
10         dst.__name__ = src.__name__
11         dst.__doc__ = src.__doc__
12         return dst
13     return _copy
14 
15 def logger(fn):
16     @copy_properties(fn) # wrapper = copy_properties(fn)(wrapper)
17     def wrapper(*args,**kwargs):
18         'I am wrapper'
19         print('begin')
20         x = fn(*args,**kwargs)
21         print('end')
22         return x
23     return wrapper
24 
25 @logger #add = logger(add)
26 def add(x,y):
27     '''This is a function for add'''
28     return x + y
29 
30 print("name = {}\ndoc = {}".format(add.__name__, add.__doc__))
31 
32 
33 
34 #以上代碼執行結果以下:
35 name = add
36 doc = This is a function for add

 

四.裝飾器案例

1>.無參裝飾器

 1 #!/usr/bin/env python
 2 #_*_coding:utf-8_*_
 3 #@author :yinzhengjie
 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
 5 #EMAIL:y1053419035@qq.com
 6 
 7 import datetime
 8 import time
 9 
10 """
11     定義一個裝飾器
12 """
13 def logger(fn):
14     def wrap(*args, **kwargs):
15         # before 功能加強
16         print("args={}, kwargs={}".format(args,kwargs))
17         start = datetime.datetime.now()
18         ret = fn(*args, **kwargs)
19         # after 功能加強
20         duration = datetime.datetime.now() - start
21         print("function {} took {}s.".format(fn.__name__, duration.total_seconds()))
22         return ret
23     return wrap
24 
25 @logger # 至關於add = logger(add),調用裝飾器
26 def add(x, y):
27     print("===call add===========")
28     time.sleep(2)
29     return x + y
30 
31 print(add(4, y=7))
32 
33 
34 
35 #以上代碼輸出結果以下:
36 args=(4,), kwargs={'y': 7}
37 ===call add===========
38 function add took 2.000114s.
39 11

2>.有參裝飾器

 1 #!/usr/bin/env python
 2 #_*_coding:utf-8_*_
 3 #@author :yinzhengjie
 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
 5 #EMAIL:y1053419035@qq.com
 6 
 7 import datetime,time
 8 
 9 def copy_properties(src): # 柯里化
10     def _copy(dst):
11         dst.__name__ = src.__name__
12         dst.__doc__ = src.__doc__
13         return dst
14     return _copy
15 
16 """
17 定義裝飾器:
18     獲取函數的執行時長,對時長超過閾值的函數記錄一下
19 """
20 def logger(duration):
21     def _logger(fn):
22         @copy_properties(fn) # wrapper = wrapper(fn)(wrapper)
23         def wrapper(*args,**kwargs):
24             start = datetime.datetime.now()
25             ret = fn(*args,**kwargs)
26             delta = (datetime.datetime.now() - start).total_seconds()
27             print('so slow') if delta > duration else print('so fast')
28             return ret
29         return wrapper
30     return _logger
31 
32 @logger(5) # add = logger(5)(add)
33 def add(x,y):
34     time.sleep(3)
35     return x + y
36 
37 print(add(5, 6))
38 
39 
40 
41 #以上代碼執行結果以下:
42 so fast
43 11
 1 #!/usr/bin/env python
 2 #_*_coding:utf-8_*_
 3 #@author :yinzhengjie
 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
 5 #EMAIL:y1053419035@qq.com
 6 
 7 import datetime,time
 8 
 9 def copy_properties(src): # 柯里化
10     def _copy(dst):
11         dst.__name__ = src.__name__
12         dst.__doc__ = src.__doc__
13         return dst
14     return _copy
15 
16 """
17 定義裝飾器:
18     獲取函數的執行時長,對時長超過閾值的函數記錄一下
19 """
20 def logger(duration, func=lambda name, duration: print('{} took {}s'.format(name, duration))):
21     def _logger(fn):
22         @copy_properties(fn) # wrapper = wrapper(fn)(wrapper)
23         def wrapper(*args,**kwargs):
24             start = datetime.datetime.now()
25             ret = fn(*args,**kwargs)
26             delta = (datetime.datetime.now() - start).total_seconds()
27             if delta > duration:
28                 func(fn.__name__, duration)
29             return ret
30         return wrapper
31     return _logger
32 
33 @logger(5) # add = logger(5)(add)
34 def add(x,y):
35     time.sleep(3)
36     return x + y
37 
38 print(add(5, 6))
39 
40 
41 
42 #以上代碼輸出結果以下:
43 11
將記錄的功能提取出來,這樣就能夠經過外部提供的函數來靈活的控制輸出

 

五.functools模塊

1>.functools概述

functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS,updated=WRAPPER_UPDATES)  
  相似copy_properties功能
  wrapper 包裝函數、被更新者,wrapped 被包裝函數、數據源
  元組WRAPPER_ASSIGNMENTS中是要被覆蓋的屬性'__module__', '__name__', '__qualname__', '__doc__', '__annotations__'模塊名、名稱、限定名、文檔、參數註解
  元組WRAPPER_UPDATES中是要被更新的屬性,__dict__屬性字典
  增長一個__wrapped__屬性,保留着wrapped函數

2>.functools模塊案例

 1 #!/usr/bin/env python
 2 #_*_coding:utf-8_*_
 3 #@author :yinzhengjie
 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
 5 #EMAIL:y1053419035@qq.com
 6 
 7 import datetime, time, functools
 8 
 9 def logger(duration, func=lambda name, duration: print('{} took {}s'.format(name, duration))):
10     def _logger(fn):
11         @functools.wraps(fn)
12         def wrapper(*args,**kwargs):
13             start = datetime.datetime.now()
14             ret = fn(*args,**kwargs)
15             delta = (datetime.datetime.now() - start).total_seconds()
16             if delta > duration:
17                 func(fn.__name__, duration)
18             return ret
19         return wrapper
20     return _logger
21 
22 @logger(5) # add = logger(5)(add)
23 def add(x,y):
24     time.sleep(1)
25     return x + y
26 
27 print(add(5, 6), add.__name__, add.__wrapped__, add.__dict__, sep='\n')
28 
29 
30 
31 
32 #以上代碼執行結果以下:
33 11
34 add
35 <function add at 0x0000000002A0F378>
36 {'__wrapped__': <function add at 0x0000000002A0F378>}
相關文章
相關標籤/搜索