先定义一个简单的函数:
def sum(x, y):
print(' x + y', eval('x + y'), sep=' = ')
sum(37, 73)
# 运行结果
# x + y = 110
现在我们假设想要增加上面函数的功能,比如对函数做一个简单的说明,但又不希望修改这个求和函数的定义。此时就需要用到装饰器(Decorator)了。
所谓装饰器,就是在代码运行期间动态增加功能的一种方式,它的本质就是一个返回函数的高阶函数。下面具体给出一个示例:
def description(func):
def des(*args, **kw_args):
print('{} is a sum function:'.format(func.__name__))
return func(*args, **kw_args)
return des
@description
def sum(x, y):
print(' x + y', eval('x + y'), sep=' = ')
sum(73, 37)
# 运行结果
# sum is a sum function:
# x + y = 110
description函数是一个decorator,所以它接受一个函数作为参数,并返回一个函数。
把@description放到sum()函数的定义处,相当于执行了语句:sum = description(sum)。
对于上述代码的解释:首先,由于description()是一个装饰器,返回一个函数,所以原始的sum()函数仍然存在,只是同名的sum变量指向了一个新的函数,于是调用sum(37, 73)将执行新函数即在description()函数中返回的des()函数。在des()函数内,首先对原始函数的说明信息,然后调用原始函数。
如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数。例如,我们现在想自定义对函数的说明信息,而不是由系统指定:
def description(information):
def des_decorator(func):
def des(*args, **kw_args):
print('%s: %s' % (func.__name__, information))
return func(*args, **kw_args)
return des
return des_decorator
@description('a sum function:')
def sum(x, y):
print(' x + y', eval('x + y'), sep=' = ')
print(sum.__name__)
sum(73, 37)
# 运行结果
# des
# sum: a sum function
# x + y = 110
上述代码调用sum(73, 37)时的情况是这样的:sum = description('a sum function')(sum)。先执行description('a sum function'),返回的是一个decorator,再调用返回的函数并传入参数sum()函数,返回值为des()函数。在des()函数内,首先对原始函数的说明信息,然后调用原始函数。
当前存在的问题:我们已在代码中说明,经过装饰器修饰后的sum()函数,sum.__name变为了des,因此我们需要对此进行修改。将原始函数达的__name__等属性复制到des()函数中,否则,有些依赖于函数签名的代码段就会出现意想不到的错误:
import functools
def description(func):
@functools.wraps(func)
def des(*args, **kw_args):
print('{} is a sum function:'.format(func.__name__))
return func(*args, **kw_args)
return des
对部分代码做如上修改即可。
示例:请编写一个decorator,能在函数调用的前后打印出'begin call'和'end call'的日志。
再思考一下能否写出decorator,使它既支持:
@multi_decorator
def f():
pass
又支持:
@multi_decorator('information')
def f(): pass
import functools
def multi_decorator(*t_args):
def m_decorator(func):
@functools.wraps(func)
def dec(*args, **kw_args):
if len(t_args) > 0:
print('%s' % t_args[0])
else:
print('begin call')
func(*args, **kw_args)
print('end call')
return dec
return m_decorator
@multi_decorator()
def sum(x, y):
print('x + y = %d' % (x + y))
@multi_decorator('execute the function')
def sum2(x, y):
print('x + y = %d' % (x + y))
sum(73, 37)
print("------- ------- -------")
sum2(73, 37)
# 运行结果
# begin call
# x + y = 110
# end call
# ------- ------- -------
# execute the function
# x + y = 110
# end call