在看python的装饰器机制之前。我们来研究一段python代码。
def process():
print("processing...")
process()
这段代码简单的执行了process()函数,process函数里面执行了一段print代码。
接着我们来拓展这个函数
def decorator_fun(func):
print("Inside decorator")
def inner(*args, **kwargs):
print("Inside inner function")
print("Decorated the function")
func()
return inner
def process():
print("processing...")
decorator_fun(process)()
我们从追踪print日志,或者用python tutor来分析下这段程序的执行脉络
从Objects列可以看到实例化对象,从Frames列看到它们的执行顺序是:
(Global frame).decorator_func
--> (f1).decorator_fun --> (f1).decorator_fun.inner
--> (inner)
--> (f1).decorator_run.Return_value
-->(Global frame).process
也就是说执行(Global frame).process之前,我们可以插入一段proprocess代码。
我们把这个decorator_fun抽象出来,然后用一种以@开头的写法来注释这段process函数。decorator主干代码写法大概是这样
def decorators(*args, **kwargs):
def inner(func):
'''
do operations with func
'''
return func
return inner #this is the fun_obj mentioned in the above content
@decorators(params)
def func():
"""
function implementation
"""
使用@方式改写的process函数如下:
def decorator_fun(func):
print("Inside decorator")
def inner(*args, **kwargs):
print("Inside inner function")
print("Decorated the function")
func()
return inner
@decorator_fun
def process():
print("processing...")
process()
这里从图中看到除了(Global frame).process指向了inner(*args, **kwargs) [parent=f],和以前的process指向不一样。其他都是一样的。就执行流程和结果来说,和之前的函数嵌套方式是一样的。
到这里总结下,使用装饰器的写法,在每个实例化process的地方都不用修改代码。这在工程重构或者复用性有着独特的优势。
我们再来升级下这个装饰器,让装饰器带上参数以完成更高级的事例。事例后面再讲,我们来说一下这种带参数的装饰器是怎么写的。
def decorators(*args, **kwargs):
def inner(func):
'''
do operations with func
'''
return func
return inner #this is the fun_obj mentioned in the above content
@decorators(params)
def func():
"""
function implementation
"""
我们在def decorator带上args和kwargs来携带tuple类型和dict类型的参数。在调用方这里加上一个小括号,放置参数。
因为这里@decorators(params)带上了小括号,所以这里会实例化这个decorators。decorators里面定义一个def inner(func),来表达Frame上的function。这个只有一层inner中间嵌套写法会导致 不用显示调用func就会 执行整套代码,包括func。为此,如果装饰器有需要携带params,我们会采用这一种写法:多层嵌套。
# Python code to illustrate
# Decorators with parameters in Python (Multi-level Decorators)
def decodecorator(dataType, message1, message2):
def decorator(fun):
print(message1)
def wrapper(*args, **kwargs):
print(message2)
if all([type(arg) == dataType for arg in args]):
return fun(*args, **kwargs)
return "Invalid Input"
return wrapper
return decorator
@decodecorator(str, "Decorator for 'stringJoin'", "stringJoin started ...")
def stringJoin(*args):
st = ''
for i in args:
st += i
return st
这里从日志输出看到第5行代码print(message2)并没有执行到。如果需要执行第5行代码进而执行第7行代码。需要这样做,加上print(stringJoin("I ",'like ',"tencent"))
def decodecorator(dataType, message1, message2):
def decorator(fun):
print(message1)
def wrapper(*args, **kwargs):
print(message2)
if all([type(arg) == dataType for arg in args]):
return fun(*args, **kwargs)
return "Invalid Input"
return wrapper
return decorator
@decodecorator(str, "Decorator for 'stringJoin'", "stringJoin started ...")
def stringJoin(*args):
st = ''
for i in args:
st += i
return st
print(stringJoin("I ", 'like ', "tencent"))
这段代码用非decorator的写法 来达到一样的执行顺序。则这样改
def decodecorator(dataType, message1, message2):
def decorator(fun):
print(message1)
def wrapper(*args, **kwargs):
print(message2)
if all([type(arg) == dataType for arg in args]):
return fun(*args, **kwargs)
return "Invalid Input"
return wrapper
return decorator
def stringJoin(*args):
st = ''
for i in args:
st += i
return st
print(decodecorator(str, "Decorator for 'stringJoin'", "stringJoin started ...")(stringJoin)("I ", 'like ', "tencent"))
我们使用python tutor查看Frame和Object如下。
我们可以用decorator方便地实现很多场景
from functools import wraps
def log_args_and_response(log_level):
def actual_decorator(func):
@wraps(func)
def log_func(*args, **kwargs):
print("begin %s(%s, %s)" % (func.__name__, args, kwargs))
res = func(*args, **kwargs)
print("end %s(%s, %s)" % (func.__name__, args, kwargs))
return res
return log_func
return actual_decorator
@log_args_and_response("debug")
def process():
print("processing...")
process()
from collections import defaultdict
import time
call_times=defaultdict(int)
time_spent=defaultdict(int)
lastprint=time.time()
def timing_decorator2(*outer_args, **outer_kwargs):
def decorator(func):
print("++++++++")
def wrapper(*args, **kwargs):
global lastprint, call_times
request_time = time.time()
res = func(*args, **kwargs)
response_time = time.time() - request_time
warn_after = outer_kwargs.get('warn_after')
if warn_after:
if response_time > warn_after:
print('%s execution took %.3f (more than %.3f)' %
(func.__name__, response_time, warn_after))
print("------")
return wrapper
return decorator
@timing_decorator2(warn_after=0.5, show_stats=True)
def example2_api():
print("=======")
time.sleep(10)
pass
example2_api()
def caller(*allowed_callers, **kwargs):
'''
@caller
check if services are allowed to call the decorated method
Returns:
The annotated function
'''
def make_check_allowed(method_name):
def check_allowed(request):
'''
determine if the request is allowed or not
'''
return True
#callers = get_caller(from_service, header)
#is_omnipotent = caller in OMNIPOTENT_SERVICES
#if is_omnipotent:
#return check_allowed
return True
def callers_decorator(func):
#func.check_allowed = make_check_allowed(func.__name__)
func.allowed_callers = allowed_callers
return func
return callers_decorator
@caller("maintence")
def example_api():
return True
example_api
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。