装饰器
首先,给出装饰器的框架:
def log(func):
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper @log
def now():
print('2018-6-14')
Python装饰器,本质上就是一个高阶函数。作用是给其它函数增加新的功能。借用python的@语法,可以将一个高阶函数定义为装饰器。
@符号的作用
但是,在学习廖雪峰老师的Python教程时,似懂非懂,尤其是看到@这个符号的时候,感觉一头雾水。现在回想来看,只要百度一下就能知道@符号在Python中的作用。它的作用就是修饰一个函数。位置在被修饰的函数的前一行,@之后是修饰函数的函数。 例如上述的 @log 起到的作用就是相当于执行了语句: now = log(now) ,现在或许难以理解这行代码的作用。接下来,进行一个对比,就能知道装饰器的威力了。
使用装饰器之前
#给下列函数增加新功能:调用函数时,打印函数名。
#要求:()不改变函数的定义,不改变函数的调用方式。
def now():
print('2015-3-25')
#增加一个高级函数
def call_name(func):
def wrapper(*args, **kw):
print('call %s()' % func.__name__)
return func(*args, **kw)
return wrapper
#调用now函数
now = call_name(now) #调用方式还是改变了(增加了一行)
now()
使用装饰器之后
#给下列函数增加新功能:调用函数时,打印函数名。
#要求:()不改变函数的定义,不改变函数的调用方式。 #写一个装饰器
def call_name(func):
def wrapper(*args, **kw):
print('call %s()' % func.__name__)
return func(*args, **kw)
return wrapper @call_name #相当于执行语句 call_name(now)
def now():
print('2015-3-25') #调用now函数
now() #这才是真正没改变调用方式
通过对比,我对装饰器的作用了解更加深刻了。下面是带参数的装饰器:
带参数的装饰器
上例,如果装饰器call_name(func)本身还要带参数,那么需要更加复杂的高阶函数。
def call_name(text): #text为装饰器的参数
def decorator(func):
def wrapper(*args, **kw):
print('%s call %s()' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator @call_name('execute ') #相当于 now = log('execute')(now)
def now():
print('2015-3-25') # 调用now函数
now()
装饰器练习
设计一个decorator(装饰器),它可作用于任何函数上,并打印该函数的执行时间:
import time import functools def metric(fn):
@functools.wraps(fn)
def wrapper(*args, **kw):
print('%s executed in %s' %
(fn.__name__, time.asctime(time.localtime(time.time()))))
return fn(*args, **kw)
return wrapper # 测试 @metric
def fast(x, y):
time.sleep(0.0012)
return x + y @metric
def slow(x, y, z):
time.sleep(0.1234)
return x * y * z f = fast(11, 22)
s = slow(11, 22, 33) print(f)
print(s) if f != 33:
print('测试失败!')
elif s != 7986:
print('测试失败!')
else:
print('测试成功!')
编写一个decorator,能在函数调用的前后打印出'begin call'和'end call'的日志:
def call_name(func):
def wrapper(*args, **kw):
print('begin call:')
print('call %s()' % func.__name__)
call = func(*args, **kw) #函数在此时调用
print('end call.')
return call
return wrapper @call_name
def now():
print('2015-3-25') # 调用now函数
now()
加大难度,写出一个@call_name
的decorator,使它既支持:
@log
def f():
pass
又支持:
@log('execute')
def f():
pass
源码实现:
import functools def call_name(text):
if isinstance(text, str): #通过参数能够判断两种模式
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('%s call %s()' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
else:
@functools.wraps(text)
def wrapper(*args, **kw):
print('call %s()' % text.__name__)
return text(*args, **kw)
return wrapper @call_name
def now():
print('2015-3-25') # 调用now函数
now()