Python 装饰器
装饰器本质上是一个 Python 函数,它可以让其他函数在不变动代码的前提下增加额外功能,装饰器的返回值也是一个函数对象。
定义函数
>>> def foo():
... print("hello")
...
>>> foo
<function foo at 0x00000000035EC268>
>>> foo()
hello
定义了一个函数,函数名 foo,也可以把 foo 理解为变量名,该变量指向一个函数对象。调用函数只需要在函数名后加上 () 并传递参数(如果函数定义的时候有参数的话)。
函数作为返回值
在 Python 中,一切皆为对象,函数也不例外,可以作为其它函数的返回值。
>>> def foo():
... print("hello")
...
>>> def bar():
... return foo
...
>>> bar()
<function foo at 0x00000000035EC268>
>>> bar()()
hello
调用函数 bar() 的返回值是一个函数对象 ,变量 foo 指向的对象与 bar() 的返回值是同一个对象。
函数作为参数
>>> def foo(num):
... return num + 1
...
>>> def bar(func):
... return func(2)
...
>>> bar(foo)
3
函数 bar() 接收一个函数对象 foo 作为参数。
函数嵌套
函数可以定义在另一个函数中,作为嵌套函数存在。
>>> def outer():
... x = 1
... def inner():
... print(x)
... inner()
...
>>> outer()
1
闭包
>>> def outer(x):
... def inner():
... print(x)
... return inner
...
>>> closure = outer(1)
>>> closure
<function outer.<locals>.inner at 0x0000000003ED9840>
>>> closure()
1
嵌套函数 inner 不再直接在函数里被调用,而是作为返回值。这里的 closure 就是一个闭包,本质上还是函数。
装饰器
先来看一个简单的例子:
def foo():
print("i am foo.")
现在有一个新的需求,希望可以记录下函数的执行日志,添加代码:
def foo():
print("i am foo.")
logging.info("foo is running.")
如果其他函数也有类似需求,该怎么做呢?我们可以定义一个函数专门处理日志,然后再处理业务代码:
def use_logging(func):
logging.info("{} is running.".format(func.__name__))
func()
def foo():
print("i am foo.")
use_logging(foo)
但是这样的话,函数 foo 的调用方式就改变了。那有没有更好的方式呢?有,就是装饰器。
简单装饰器
def use_logging(func):
def wrapper(*args,**kwargs):
logging.info("{} is running.".format(func.__name__))
func(*args,**kwargs)
return wrapper
def foo():
print("i am foo.")
foo = use_logging(foo)
foo()
foo 函数的代码没有变动,只是给 foo 变量重新赋值了,指向一个新的函数对象。
这里的 use_logging 函数其实就是一个装饰器,装饰器将函数作为参数传递给另一个函数,外部函数返回内部函数对象。
@符号是装饰器的语法糖,在定义函数时使用,省去了重新赋值操作。
def use_logging(func):
def wrapper(*args,**kwargs):
logging.info("{} is running.".format(func.__name__))
func(*args,**kwargs)
return wrapper
@use_logging # 等效于 foo = use_logging(foo)
def foo():
print("i am foo.")
foo()
带参数的装饰器
在上面的装饰器调用中,@use_logging 的参数是执行业务的函数 foo 。装饰器的语法允许我们在调用时提供其他参数,比如 @decorator(func)。
def use_logging(level):
def decorator(func):
def wrapper(*args,**kwargs):
if level == "info":
logging.info("{} is running.".format(func.__name__))
func(*args,**kwargs)
return wrapper
return decorator
@use_logging("info")
def foo(name="foo"):
print("i am {}.".format(name))
foo()
类装饰器
class Test(object):
def __init__(self,func):
self.func = func
def __call__(self,*args,**kwargs):
print("{} is running.".format(self.func.__name__))
self.func(*args,**kwargs)
@Test # 等效于 test = Test(test)
def test():
print("This is text.")
test()
实例化对象时使用 __init__ 方法接收需要装饰的函数,定义 __call__ 方法运行装饰的函数代码。
内置装饰器
@staticmathod,@classmathod,@property
装饰器顺序
@desc1(args)
@desc2
@desc3
def func():
pass
等效于: f = desc1(args)(desc2(desc3(func)))