写在前面
首先理解下,python的装饰器@mydec
是一个语法糖,它的本质是func = mydec(func)
,也就是说把函数func
作为参数传入在mydec
中。
引子
假设一个函数要打印用户的姓名、年龄、性别当然还有其他Blablabla…
def print_human_information(name, age, sex):
print("name is {}, age is {}, sex is {}.".format(name, age, sex))
if __name__ == "__main__":
print_human_information(name="Lee", age=-10, sex='unknown')
然后突然你想看下这个函数执行花了多少时间,可能你会这么写:
import time
def print_human_information(name, age, sex):
start = time.clock()
print("name is {}, age is {}, sex is {}.".format(name, age, sex))
end = time.clock()
print "execute in {:.2f} ms".format((end - start) * 1000)
if __name__ == "__main__":
print_human_information(name="Lee", age=-10, sex='unknown')
增加一个start,一个end,完美!
但是,如果再有一个函数不打印human,打印animal呢?Ctrl+C、V?太low逼!我们有更好的办法解决这个问题。
import time
def timeused(function):
def wrapper(name, age, sex):
start = time.clock()
function(name, age, sex)
end = time.clock()
print "execute in {:.2f} ms".format((end - start) * 1000)
return wrapper
def print_human_information(name, age, sex):
print("name is {}, age is {}, sex is {}.".format(name, age, sex))
# 看这里,看这里,看这里
# 重要的事情说三遍
print_human_information = timeused(print_human_information)
if __name__ == "__main__":
print_human_information(name="Lee", age=-10, sex='unknown')
现在只要有了print_human_information = timeused(print_human_information)
在main
函数中不需要做任何修改就可以显示函数执行的时间,接着让我们再来一个更简洁的语法。
@timeused
def print_human_information(name, age, sex):
…
完美!
进阶
现在打印animal的函数真的来了,然而参数个数只有两个:
@timeused
def print_animal_information(name, age):
print("name is {}, age is {}.".format(name, age))
好吧,timeused报错了,修改内嵌函数以及function的参数形式如下,支持任意多个参数:
def timeused(function):
def wrapper(*args, **kwargs):
start = time.clock()
function(*args, **kwargs)
end = time.clock()
print "execute in {:.2f} ms".format((end - start) * 1000)
return wrapper
God祥曾经教导我们:
一旦满足了某个需求,那么接下来类似的需求就会接踵而至。
很快,我们需要一个装饰器来检查print_human_information
或print_animal_information
中age
和sex
的合法性。(如年龄不能为负数,人类性别只能是male
或者female
,而对于动物我们不关心,因此该项不检查)
或许,现在是时候编写一个包含参数的装饰器了!
# 默认不检查年龄和性别
def check(check_age = False, check_sex = False):
def wrapper(function):
def _wrapper(*args, **kwargs):
if check_age:
_age = kwargs['age']
if _age < 0 or _age > 140:
print "invalid age '{}', use default {}.".format(_age, 0)
kwargs['age'] = 0
if check_sex:
_sex = kwargs['sex']
if _sex != 'male' or _sex != 'female':
print "invalid sex '{}', use default {}.".format(_sex, 'male')
kwargs['sex'] = 'male'
function(*args, **kwargs)
# 用于展示装饰器嵌套的效果
time.sleep(0.5)
return _wrapper
return wrapper
@check(check_age = True, check_sex = True)
@timeused
def print_human_information(name, age, sex):
…
@timeused
@check(check_age = True)
def print_animal_information(name, age):
…
资料
Python装饰器学习(九步入门)
PEP 0318 -- Decorators for Functions and Methods
PythonDecoratorLibrary