前言
這篇文章打算寫下關(guān)于python3中裝飾器的一些認識,提高一下知識水平
1 裝飾器是啥
裝飾器本質(zhì)上是一個 Python 函數(shù)/類氓皱,它可以讓其他函數(shù)/類在不需要做任何代碼修改的前提下增加額外功能讳窟,裝飾器的返回值也是一個函數(shù)/類對象茄蚯。它經(jīng)常用于有切面需求的場景算吩,比如:插入日志哥桥、性能測試碍沐、權(quán)限校驗等場景狸捅,裝飾器是解決這類問題的絕佳設(shè)計。有了裝飾器累提,我們就可以抽離出與函數(shù)功能本身無關(guān)的代碼到裝飾器中復(fù)用尘喝。概括的講,裝飾器的作用就是為已經(jīng)存在的對象添加額外的功能斋陪。
2 簡單裝飾器(不使用@符號)
下面是一個簡單的裝飾器例子朽褪,log_append 來裝飾 direct_print,這種寫法支持任意參數(shù)的direct_print鳍贾,也支持任意參數(shù)的裝飾函數(shù)log_append
def log_append():
def decorator(func):
def wrapper(**kwargs):
if "name" in kwargs:
print("check name %s" % (kwargs.get("name")))
else:
print("check name fail" )
return
return func(**kwargs)
return wrapper
return decorator
def direct_print(**kwargs):
print("print name is %s"% (kwargs.get("name")))
fun_decoratored=log_append()(direct_print)
fun_decoratored(names="111")
fun_decoratored(name="111")
print(fun_decoratored)
direct_print(name="111")
3 用@符號
利用@符號鞍匾,可以提高代碼的閱讀性,我們用@的方式改寫上面的代碼
def log_append():
def decorator(func):
def wrapper(**kwargs):
if "name" in kwargs:
print("check name %s" % (kwargs.get("name")))
else:
print("check name fail" )
return
return func(**kwargs)
return wrapper
return decorator
@log_append()
def direct_print(**kwargs):
print("print name is %s"% (kwargs.get("name")))
direct_print(name="111")
direct_print(names="111")
4 裝飾器函數(shù)傳遞參數(shù)并體現(xiàn)裝飾器便利性
假如現(xiàn)在接口強制校驗的規(guī)則不定,一個函數(shù)強制校驗name骑科,另一個強制校驗names字段橡淑,后面可能還會強制校驗key等別的字段,這時候改寫log_append就比較方便了
下面的代碼在完全不改變direct_print 業(yè)務(wù)邏輯的情況下咆爽,重寫了校驗邏輯梁棠,并且
復(fù)用在direct_print_names 方法
from collections import Iterable
def log_append(au_keys=[]):
def decorator(func):
def wrapper(**kwargs):
if not isinstance(au_keys,Iterable):
raise Exception("error au_keys,should be Iterable str")
for check_key in au_keys:
if check_key in kwargs:
print("check %s %s" % (check_key,kwargs.get(check_key)))
else:
print("check %s fail\n"% (check_key))
return
return func(**kwargs)
return wrapper
return decorator
@log_append(["name"])
def direct_print(**kwargs):
print("print name is %s\n"% (kwargs.get("name")))
@log_append(["names"])
def direct_print_names(**kwargs):
print("print names is %s\n"% (kwargs.get("names")))
direct_print(name="111")
direct_print(names="111")
direct_print_names(name="111")
direct_print_names(names="111")
5 functools.wraps 保留函數(shù)信息
不使用functools.wraps會導(dǎo)致 docstring、name這些函數(shù)元信息丟失斗埂,比如 使用 direct_print_names.name 會返回wrapper符糊,這樣很影響使用,所以我們需要functools.wraps呛凶。加入方式簡單男娄,再裝飾函數(shù)傳入func的地方加入@wraps
from collections import Iterable
from functools import wraps
def log_append(au_keys=[]):
def decorator(func):
@wraps(func)
def wrapper(**kwargs):
print()
if not isinstance(au_keys,Iterable):
raise Exception("error au_keys,should be Iterable str")
for check_key in au_keys:
if check_key in kwargs:
print("check %s %s" % (check_key,kwargs.get(check_key)))
else:
print("check %s fail\n"% (check_key))
return
return func(**kwargs)
return wrapper
return decorator
@log_append(["name"])
def direct_print(**kwargs):
print("print name is %s\n"% (kwargs.get("name")))
@log_append(["names"])
def direct_print_names(**kwargs):
print("print names is %s\n"% (kwargs.get("names")))
direct_print(name="111")
direct_print(names="111")
direct_print_names(name="111")
direct_print_names(names="111")
print(direct_print.__name__)
6 調(diào)用順序
python支持多重裝飾的,使用方式就是一個一個的@寫下去,它的執(zhí)行順序是從外到里模闲,最先調(diào)用最外層的裝飾器建瘫,最后調(diào)用最里層的裝飾器 在上面的代碼里面加入打印時間的裝飾函數(shù)print_time,讓direct_print 先裝飾print_time,direct_print_names后裝飾尸折,實際上 direct_print 效果等于
print_time(log_append(["name"])(direct_print))
direct_print_names效果等于
log_append(["_names"])(print_time(direct_print))
整體代碼如下
from collections import Iterable
from functools import wraps
import time
def log_append(au_keys=[]):
def decorator(func):
@wraps(func)
def wrapper(**kwargs):
print()
if not isinstance(au_keys,Iterable):
raise Exception("error au_keys,should be Iterable str")
for check_key in au_keys:
if check_key in kwargs:
print("check %s %s" % (check_key,kwargs.get(check_key)))
else:
print("check %s fail\n"% (check_key))
return
return func(**kwargs)
return wrapper
return decorator
def print_time(func):
@wraps(func)
def wrapper(**kwargs):
print("time is %s"%(time.strftime("%Y-%m-%d %H:%M;%S",time.localtime())))
return func(**kwargs)
return wrapper
@print_time
@log_append(["name"])
def direct_print(**kwargs):
print("print name is %s\n"% (kwargs.get("name")))
@log_append(["names"])
@print_time
def direct_print_names(**kwargs):
print("print names is %s\n"% (kwargs.get("names")))
direct_print(name="111")
direct_print(names="111")
direct_print_names(name="111")
direct_print_names(names="111")
print(direct_print.__name__)
結(jié)語
這篇文章看下來啰脚,python的裝飾器基本上差不多了吧,好好學(xué)習(xí)实夹,天天向上 ^_^