Python装饰器的作用
用于增强扩展Python函数或类功能的一种语法糖,类似与装饰者模式,还具备解耦的功能。
此语法糖在Python使用极其广泛,Python开发必备技能之一,常见使用于日志,性能检测,权限判断等。
以下用几个例子,由浅入深的了解装饰器。
Python装饰器的类型
函数装饰器
函数装饰器分为两种,不带参数的与带参数的。
最简单的装饰器
# 例子1 一个最简单的装饰器
import time
# 自定义一个名为simple_decorator的装饰器
def simple_decorator(func): # func代表被装饰的函数
def wrapper(*args, **kwargs): # *args, **kwargs代表被装饰函数的参数
return func()
return wrapper
# 使用simple_decorator装饰器装饰doing函数
@simple_decorator
def doing():
time.sleep(1)
doing()
不带参数的函数装饰器
# 例子2 在例子1的基础上增加对函数调用时间的输出功能
import time
def simple_decorator(func):
def wrapper(*args, **kwargs):
call_begin_time = time.time()
func()
cost_time = time.time() - call_begin_time
print(f'调用{func.__name__}函数花费了:{cost_time}秒。')
return wrapper
@simple_decorator
def doing():
time.sleep(1)
doing()
==============================
输出结果:
调用doing函数花费了:1.012902021408081秒。
进程已结束,退出代码为 0
带参数的函数装饰器
# 例子3 带参数的函数装饰器需要套3层,我们在例子2的基础上做下修改。
import time
def calc_call_time(min_output_second): # min_output_second是函数装饰器的参数
def wrapper(func): # func代表被装饰的函数
def deco(*args, **kwargs): # *args, **kwargs代表被装饰函数的参数
call_begin_time = time.time()
func(*args, **kwargs)
cost_time = time.time() - call_begin_time
if cost_time > min_output_second:
print(f'调用{func.__name__}函数大于{min_output_second}秒,花费了: {cost_time}秒。')
else:
print(f'调用{func.__name__}函数小于{min_output_second}秒,花费了: {cost_time}秒。')
return deco
return wrapper
@calc_call_time(min_output_second=2)
def doing(sleep_time):
time.sleep(sleep_time)
doing(1)
doing(2)
==============================
输出结果:
调用doing函数小于2秒,花费了: 1.0076000690460205秒。
调用doing函数大于2秒,花费了: 2.0079410076141357秒。
进程已结束,退出代码为 0
类方法装饰器
# 例子4 依然实现调用计时功能,整体与普通函数装饰器区别不大,差别在于需要将类方法的self进行传递。
import time
def calc_call_time(min_output_second):
def wrapper(func):
def deco(instance, *args, **kwargs): # instance对象实例(即装饰类方法的self)
call_begin_time = time.time()
func(instance, *args, **kwargs)
cost_time = time.time() - call_begin_time
if cost_time > min_output_second:
print(f'调用{func.__name__}函数大于{min_output_second}秒,花费了: {cost_time}秒。')
else:
print(f'调用{func.__name__}函数小于{min_output_second}秒,花费了: {cost_time}秒。')
return deco
return wrapper
class Test(object):
@calc_call_time(min_output_second=1)
def doing(self):
time.sleep(1)
Test().doing()
==============================
输出结果:
调用doing函数大于1秒,花费了: 1.0066580772399902秒。
进程已结束,退出代码为 0
装饰器类(用类装饰一个函数)
装饰器类有两种写法,分别用于不同的场景。分别是可调用对象与全局对象。
可调用对象一般用于单个装饰器代码较为复杂的情况,需要独立成一个类来进行编写。
全局对象则用于需要管理器对各种变量进行缓存维护的场景。
在下面的例子中可以体会到这两者的不同。
可调用对象
# 例子5 这种装饰器类需要实现__init__与__call__两个魔术方法
# __init__用于传参,__call__回调函数
import time
class TimeCalcDecorator(object):
def __init__(self, min_output_second):
self.min_output_second = min_output_second
def __call__(self, func):
def wrapper(*args, **kwargs):
call_begin_time = time.time()
func(*args, **kwargs)
cost_time = time.time()-call_begin_time
if cost_time > self.min_output_second:
print(f'调用{func.__name__}函数大于'
f'{self.min_output_second}秒,花费了: {cost_time}秒。')
else:
print(f'调用{func.__name__}函数小于'
f'{self.min_output_second}秒,花费了: {cost_time}秒。')
return wrapper
@TimeCalcDecorator(min_output_second = 1)
def doing():
time.sleep(1)
doing()
==============================
输出结果:
调用doing函数大于1秒,花费了: 1.0096430778503418秒。
进程已结束,退出代码为 0
全局对象
# Flask框架的默认代码,极佳的展示了这种写法的优点
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'This Index Page'
# 例子6 使用装饰器类实现函数计时功能,并统计调用次数与调用时间。
import time
class CallFuncTimeTool(object):
def __init__(self):
self.call_func_total_second = 0 # 函数调用总时间
self.call_func_total_count = 0 # 函数调用总次数
def calc_call_time(self, min_output_second):
def wrapper(func):
def deco(*args, **kwargs):
call_begin_time = time.time()
func(*args, **kwargs)
cost_time = time.time() - call_begin_time
self.call_func_total_count += 1 # 新增代码,累计调用次数
self.call_func_total_second += cost_time # 新增代码,累计调用时间
if cost_time > min_output_second:
print(f'调用{func.__name__}函数花费了: {cost_time}秒。')
return deco
return wrapper
def output_info(self):
"""输出累计的调用次数与累计的调用时间"""
print(f'总花费{self.call_func_total_second}秒'
f'总调用了{self.call_func_total_count}次')
tool = CallFuncTimeTool()
@tool.calc_call_time(1)
def doing():
time.sleep(1)
doing()
doing()
doing()
tool.output_info()
==============================
输出结果:
调用doing函数花费了: 1.0121991634368896秒。
调用doing函数花费了: 1.0110533237457275秒。
调用doing函数花费了: 1.0152339935302734秒。
总花费3.0384864807128906秒总调用了3次
进程已结束,退出代码为 0
类的装饰器(用函数装饰一个类)
类装饰器用于对现有类修改功能或新增功能,例如对一个类添加额外的属性与方法。
该装饰器将会在类实例化时进行调用。
# 例子7 以下
instances = {}
def singleton(cls):
def get_instance(*args, **kwargs):
cls_name = cls.__name__
if not cls_name in instances:
instance = cls(*args, **kwargs)
instances[cls_name] = instance
return instances[cls_name]
return get_instance
@singleton
class User:
pass
print(id(User()))
print(id(User()))
print(id(User()))
==============================
输出结果:
1887626427016
1887626427016
1887626427016
进程已结束,退出代码为 0