python装饰器
python的装饰器一直用的都比较少,也不是很理解,因此特意学习了一下,以后可以逐渐尝试着去使用。
一、什么是装饰器
“装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。”
二、如何编写自己的装饰器?
让我们来编写一个比较简单的装饰器,在Python里面代码看起来会是这样的:
#!/usr/bin/env python
def deco(func):
def wrapper():
print "Wrap start"
func()
print "Wrap end\n"
return wrapper
@deco
def foo():
print "In foo():"
foo()
运行起来是这个样子的:
$ python decorate.py
Wrap start
In foo():
Wrap end
我们可以在“Wrap start”和“Wrap end”里面做一些自己想要的功能,比如计算运行时间,输出日志等等。
三、为什么要返回一个函数?
我第一次看到“return wrapper”这句的时候,就在想为什么要返回一个函数?
分析一下代码:
def deco(func):
print func
return func
@deco
def foo():pass
foo()
运行的结果看起来很完美,但是只是看起来。让我们把代码修改一下:
>>> def deco(func):
... print "In deco"
... return func
...
>>> @deco
... def foo():
... print "In foo"
...
In deco
>>>
等等,代码里面还没有调用 foo(),怎么就print “In deco”了?
这里编写的装饰器根本就没有达到我们要求的功能,因为它返回的还是 func 本身,print “In deco” 也只会在初始化的时候运行一次,仅仅一次。
让我们回到刚开始的例子,在 deco 里面返回了一个 wrapper 函数对象。可以试着这么理解,deco的作用是给 func 进行装饰,wrapper 就是被装饰过的func。
然后我们就可以重复调用这个被装饰过的函数。
四、怎么装饰一个 需要传参数 的函数?
现在另一个问题又来了,前面被装饰的函数都是不带参数的,那带参数的函数要怎么装饰呢?
让我们先尝试下前面的办法:
>>> def deco(func):
... def wrapper():
... print "Wrap start"
... func()
... print "Wrap end\n"
... return wrapper
...
>>> @deco
... def foo(x):
... print "In foo():"
... print "I have a para: %s" % x
...
>>> foo('x')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: wrapper() takes no arguments (1 given)
码
报了个缺少参数的错误,那把这个参数带上:
>>> def deco(func):
... def wrapper(x):
... print "Wrap start"
... func(x)
... print "Wrap end\n"
... return wrapper
...
>>> @deco
... def foo(x):
... print "In foo():"
... print "I have a para: %s" % x
...
>>> foo('x')
Wrap start
In foo():
I have a para: x
Wrap end
现在可以正常传递参数了。
五、怎么装饰 参数列表不一样 的多个函数?
继续发散一下,要是想装饰多个函数,但是这些函数的参数列表变化很大的呢?
这个时候,就到了使用Python参数魔法的时候了。
定义函数时:*params:收集其余的位置参数,返回元组。 **params:收集其余的关键字参数,返回字典。
调用函数时:*params:将元组拆分为位置参数传入。 **params:将字典拆分为关键字参数传入。
利用上面的参数魔法后,代码看起来会是这样的:
#!/usr/bin/env python
def deco(func):
def wrapper(*args, **kwargs):
print "Wrap start"
func(*args, **kwargs)
print "Wrap end\n"
return wrapper
@deco
def foo(x):
print "In foo():"
print "I have a para: %s" % x
@deco
def bar(x,y):
print "In bar():"
print "I have two para: %s and %s" % (x, y)
@deco
def foo_dict(x,z='dict_para'):
print "In foo_dict:"
print "I have two para, %s and %s" % (x, z)
if __name__ == "__main__":
foo('x')
bar('x', 'y')
foo_dict('x', z='dict_para')
运行一下看看效果:
$ python decorate.py
Wrap start
In foo():
I have a para: x
Wrap end
Wrap start
In bar():
I have two para: x and y
Wrap end
Wrap start
In foo_dict:
I have two para, x and dict_para
Wrap end