练习一
请编写一个decorator,能在函数调用的前后打印出'begin call'
和'end call'
的日志。
这部分并不难,在wrapper中将函数的运行值赋值给一个变量,再返回该变量即可,如下:
import functools
def log(fn):
@fuctools.wraps(fn)
def wrapper(*args,**kw):
print ('begin call')
x=fn(*args,**kw)
print('begin call')
return x
return wrapper
练习二
再思考一下能否写出一个@log
的decorator,使它既支持:
@log
def f():
pass
又支持:
@log('execute')
def f():
pass
这个练习的关键点在于要使得log在有参数和无参数的情况下都有对应的执行,这一点在C++中是可以通过重载函数解决的,但是在python中还没接触到,所以没能想出来
参考答案如下:
def log(text):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('Begin call')
print('calling func: %s' % func.__name__)
r = func(*args, **kw)
print('Eng call')
return r
return wrapper
return decorator if isinstance(text, str) else decorator(text)
这里最关键的一步是为不同的输入选择合适的执行方式。
引用下廖雪峰老师原文最精彩的部分:
把
@log
放到now()
函数的定义处,相当于执行了语句:now = log(now)
和两层嵌套的decorator相比,3层嵌套的效果是这样的:
扫描二维码关注公众号,回复: 5286646 查看本文章>>> now = log('execute')(now)
我们需要做的就是理解并将它们用条件判断表示出来!
当log()
函数输入的是字符串,就切换为就是带参数的装饰器)
如果log()
函数输入的不是字符串,就切换为不带参数的装饰器。(当然这里没有考虑其他情况,实际使用时还应进行拓展)
但是这个参考答案带来一个疑问:decorator仅有一个定义即decorator(func),那么最后return的 decorator情况下,字符串难道可以作为函数来执行decorator定义中的语句?
不过也有收获:get了 return A if xxx else return B的操作