程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

python進階:搞懂裝飾器和切面編程

編輯:Python

學習目標:

裝飾器、切面編程,實際使用體會。
紙上學來終覺淺


裝飾器和切面編程:

常見的函數,我們一般的返回值一個常見的數值或者列表,比如:

def mysum(a,b):
return a + b
result = mysum(1,2)
print(result)

mysum函數返回的是整數1+2的值,為3.
但是python神奇的地方就是,函數的返回值,可以是另一個函數。比如:

def mysum_log():
print('input two number for sum ')
def mysum(a,b):
return a + b
return mysum
mysum2= mysum_log()
print(mysum2(1,2))

這樣,mysum_log的函數值是一個函數,賦給了變量mysum2,然後print直接用mysum2(1,2)就可以運行函數.此時的mysum2函數有點類似於mysum的功能,但是增加了print功能。
運行結果:

input two number for sum
3

我們其實可以把變量mysum2直接改成mysum,改一下名字而已,結果就有點神奇了:

def mysum_log():
print('input two number for sum ')
def mysum(a,b):
return a + b
return mysum
mysum= mysum_log()
print(mysum(1,2))

這樣我們看起來像調用了mysum函數,但實際上我們還多了打印日志的print功能。業界成這個叫閉包–鬼知道為啥要叫這麼個名字,還以為包pi有關。
這有什麼用呢?單純看沒啥用,但是當這個mysum方式最初是別人寫的,而且別人不願意改他的函數,可以通過這種方式豐富函數的功能。
舉個例子,隔壁老王寫了個函數:

def laowangsum(a, b):
return a ^ 2 + b ^ 2

laowangsum的功能是求平方和,但是沒有解釋,會讓人產生誤解,你叫老王修改函數,增加注釋,他懶不願意動,怎麼辦?
此時你想到能不能偷梁換柱,幫他增加點注釋,讓你的函數調用laowangsum會添加注釋log。於是:

def laowangsum(a, b):
return a ^ 2 + b ^ 2
def mysum_log():
print('老王寫的sum函數,實際是兩個數平方和 ')
return laowangsum
laowangsum = mysum_log()
print(laowangsum(1, 2))

你調用的laowangsum函數,實際上多運行了你的注釋,偷偷改了老王的函數的功能。這個是閉包的作用。
另一個需求:我們要求對每個函數都打印出日志信息,以便於錯誤時定位是哪個函數出了問題。或者進行函數大屏監控,怎麼辦?
為每個函數都加入一個寫log的代碼?重復性太高,我們想辦法抽象起來,既然閉包可以改變函數功能,那用閉包就可以實現,但是現在的閉包需要寫明具體函數,不可能以後新增一個函數都去改一下閉包函數。此時就需要裝飾器,也叫切面編程。簡單來講,我把一個代表函數的變量傳進閉包函數,然後這個閉包函數就可以自適應傳進去的函數,自動調用它,不用每次新增函數都去修改閉包,只用調用一次閉包函數即可。

import functools
def log_decorator(function):
@functools.wraps(function)
def wrapper(*args,**kwargs):
print('log begin')
function(*args,**kwargs)
print('log end')
return wrapper

比如老王的函數定義時,加上一個@log_decorator,就可以自動幫他寫日志,他不用管日志這塊:

import functools
def log_decorator(function):
@functools.wraps(function)
def wrapper(*args,**kwargs):
print('log begin')
result = function(*args,**kwargs)
print('log end')
return result
return wrapper
@log_decorator
def laowangsum(a, b):
return a ^ 2 + b ^ 2
print(laowangsum(1, 2))

運行結果:

log begin
log end
7

可以看到老王雖然自己只實現了加法邏輯,但是函數運行時,卻自動多了寫日志的功能,就像函數被砍了兩刀,自動加上了寫log的功能一樣。所以叫切面編程。
而且用print(laowangsum.name)打印出來,laowangsum確實就是老王自己定義的函數,名字沒有變化,沒有被偷梁換柱,因為是@functools.wraps(function)的原因。


裝飾類:

除了函數,還可以用裝飾類,大同小異。把函數替換成類

class log:
def __init__(self,func):
self.func = func
self.num_calls = 0 #函數調用次數
def __call__(self, *args, **kwargs):
self.num_calls += 1
print('number of calls is :{}'.format(self.num_calls))
return self.func(*args, **kwargs)
@log
def example():
print('I am being called')
example()
example()
example()

結果:

number of calls is :1
I am being called
number of calls is :2
I am being called
number of calls is :3
I am being called

log類自動幫助統計了函數被調用次數。


結論:

裝飾器經常用在:
1、 寫日志
2、統計運行時間
3、 校驗輸入數據的有效性和用戶認證等


  1. 上一篇文章:
  2. 下一篇文章:
相關文章
    没有相关文章
Copyright © 程式師世界 All Rights Reserved