我们将通过举例一个小函数,通过使用装饰器增加功能(实现方法不一定最好,这里仅仅是为了说明装饰器用法,不要钻牛角尖)。
1.最简单的函数
这是一个最简单的函数,打印这个计算的结果和开始结束时间(此处计算量很小,几乎同时完成,所以开始结束时间一样没有关系)
源码:
# coding: utf-8
import time
def do():
print 'start:', time.time()
result = 1 + 1
print 'result:', result
print 'end:', time.time()
do()
输出:
start: 1508143969.67
result: 2
end: 1508143969.67
2.使用装饰器增加功能,打印函数执行开始和结束时间
在python中,函数是可以作为参数传递给另一个函数的。装饰器其实就是以一个函数为参数的普通函数,然后又返回一个函数。 请注意此例的注释和执行结果
- 包装器接受一个函数作为参数,又返回一个新的函数
- 包装的过程是在程序启动时就执行的,直接返回了新函数替换了def处的函数,并非等到执行do()才替换该函数
源码:
# coding: utf-8
import time
def print_run_time(func): # 接受一个函数作为参数
def wrapper(): # 构造的新的函数,用于返回替换函数func
print 'start:', time.time()
func()
print 'end:', time.time()
return wrapper # 注意没有后缀(),因为要返回构造的新函数,而不是新函数调用结果
def do():
result = 1 + 1
print 'result:', result
print u'调用原始的do函数:'
do()
print u'根据装饰器的定义手动调用,用装饰器包装do函数:'
print_run_time(do)()
print u'根据装饰器的定义手动调用,用装饰器包装do函数(分步骤调用,思考整个调用过程和原理):'
new_do = print_run_time(do) # 把do函数作为参数,注意do没有后缀()
print u'装饰器返回一个函数', type(new_do)
new_do() # 调用装饰后的新函数
输出:
调用原始的do函数:
result: 2
根据装饰器的定义手动调用,用装饰器包装do函数:
start: 1508145089.23
result: 2
end: 1508145089.23
根据装饰器的定义手动调用,用装饰器包装do函数(分步骤调用,思考整个调用过程和原理):
装饰器返回一个函数 <type 'function'>
start: 1508145089.23
result: 2
end: 1508145089.23
3.使用语法糖包装函数
@语法糖相当于new_do = print_run_time(do)
源码:
# coding: utf-8
import time
def print_run_time(func):
def wrapper():
print 'start:', time.time()
func()
print 'end:', time.time()
return wrapper
@print_run_time
def do():
result = 1 + 1
print 'result:', result
do()
输出:
start: 1508145584.21
result: 2
end: 1508145584.21
4.包装带参数的函数
do函数改为需要输入x,y然后计算结果
源码:
# coding: utf-8
import time
def print_run_time(func):
def wrapper(x, y, *args, **kwargs): # wrapper为构造的新函数,那么原来func的参数都被传递给了wrapper
print 'start:', time.time()
func(x, y, *args, **kwargs)
print 'end:', time.time()
return wrapper
@print_run_time
def do(x, y):
result = x + y
print 'result:', result
do(1, 2)
输出:
start: 1508145970.8
result: 3
end: 1508145970.8
5.带参数的装饰器
之前输出的时间是时间戳,现在我们要根据装饰器的参数来决定打印时间戳或者日期时间。 这就需要一个工厂函数(根据参数创建一个装饰器,然后再去包装原始的do函数),原理同以上将的装饰器,不再赘述,仔细对比代码变化即可
源码:
# coding: utf-8
import time, datetime
def print_run_time(time_type): # 多了这一层来根据参数创建一个装饰器
def wrapper_factory(func): # 根据参数构造的装饰器
def wrapper(x, y, *args, **kwargs):
if time_type == 'datetime':
print 'start:', datetime.datetime.now()
func(x, y, *args, **kwargs)
print 'end:', datetime.datetime.now()
else:
print 'start:', time.time()
func(x, y, *args, **kwargs)
print 'end:', time.time()
return wrapper
return wrapper_factory
@print_run_time('datetime')
def do(x, y):
result = x + y
print 'result:', result
do(1, 2)
输出:
start: 2017-10-16 17:37:25.286000
result: 3
end: 2017-10-16 17:37:25.286000
6.带参数的装饰器(使用类的__call__
)
以上带参数装饰器使用工厂函数,多了一层更加使人迷惑,其实原理不变,这里把装饰器工厂函数用类来实现更加清晰明了
源码:
# coding: utf-8
import time, datetime
class print_run_time:
def __init__(self, time_type): # 装饰器参数
self.time_type = time_type
# __call__使实例变为可调用对象,相当于一个函数
def __call__(self, func):
def wrapper(x, y, *args, **kwargs):
if self.time_type == 'datetime':
print 'start:', datetime.datetime.now()
func(x, y, *args, **kwargs)
print 'end:', datetime.datetime.now()
else:
print 'start:', time.time()
func(x, y, *args, **kwargs)
print 'end:', time.time()
return wrapper
@print_run_time('datetime')
def do(x, y):
result = x + y
print 'result:', result
do(1, 2)
输出:
start: 2017-10-16 17:45:04.094000
result: 3
end: 2017-10-16 17:45:04.094000
原文地址:https://my.oschina.net/watcher/blog/1551500