前言
这节先复习一下装饰器,因为接下来针对一些复杂接口的响应统一使用装饰器进行处理。
函数核心
-
函数是对象。可以把函数赋予变量
def get_message(message): print(message) a = get_message a("hello python") -
函数可以当作参数,传给另一个函数
def get_message(message): print(message) def call(func, message): func(message) call(get_message, "hello python") -
函数中可以定义函数,函数的嵌套
def func(message): def get_message(message): print(message) return get_message(message) func("hello python") -
函数的返回值可以是函数对象,闭包
def func(): def get_message(message): print(message) return get_message fu = func() fu("hello python")
简单的装饰器
def my_decorator(func):
def wrapper():
print('我是装饰器')
func()
return wrapper
def greet():
print('hello python')
gre = my_decorator(greet)
gre()
变量gre指向内部函数wrapper,wrapper中又调用原函数greet。把真正需要执行的函数greet包裹在my_decorator中,并且改变了它的行为,但原函数greet不变。
上述代码,简单优雅的表示
def my_decorator(func):
def wrapper():
print('我是装饰器')
func()
return wrapper
@my_decorator
def greet():
print('hello python')
greet()
这两段代码可以看出,@my_decorator等价于gre = my_decorator(greet)
带有参数的装饰器
def my_decorator(func):
def wrapper(*args, **kwargs):
print('wrapper of decorator')
func(*args, **kwargs)
return wrapper
带有自定义参数的装饰器
案例:定义一个参数,表示装饰器内部函数被执行的次数
def repeat(num):
def my_decorator(func):
def wrapper(*args, **kwargs):
for i in range(num):
print('我是装饰器')
func(*args, **kwargs)
return wrapper
return my_decorator
@repeat(4)
def greet(message):
print(message)
greet('hello python')
'''
我是装饰器
hello python
我是装饰器
hello python
我是装饰器
hello python
我是装饰器
hello python
'''
原函数变了,怎么处理
print(greet.__name__) # wrapper
help(greet)
'''
Help on function wrapper in module __main__:
wrapper(*args, **kwargs)
'''
输出结果可以看到,函数被装饰后,元信息改变了,从greet变为了wrapper
为了保留原函数的元信息,我们需要使用内置的装饰器@functools.wrap
import functools
def my_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print('我是装饰器')
func(*args, **kwargs)
return wrapper
@my_decorator
def greet(message):
print(message)
print(greet.__name__) # greet
help(greet)
'''
Help on function greet in module __main__:
greet(message)
'''
不改变元信息有啥意义呢?我觉得如果多个函数被同一装饰器装饰,除了问题,就能找到哪个函数有问题
类装饰器
类装饰器主要依赖于函数__call__(),每当你调用一个类的实例时,函数__call__()就会被执行一次。
class Count:
def __init__(self, func):
self.func = func
self.num_calls = 0
def __call__(self, *args, **kwargs):
self.num_calls += 1
print('num of calls is: {}'.format(self.num_calls))
return self.func(*args, **kwargs)
@Count
def example():
print("hello world")
example()
'''
num of calls is: 1
hello world
'''
当我们使用 @Count 修饰函数 example 时,相当于将函数 example 作为参数传入 Count 类中构造一个对象,并将该对象重定义为新的 example 函数。由于 Count 类实现了 __call__ 方法,因此这个新的 example 函数在被调用时实际上是调用了 Count 类的 __call__ 方法。
在 __call__ 方法中,使用 self.num_calls 访问和修改了 Count 类的实例变量 num_calls 的值。由于在每次调用新的 example 函数时,都会执行一次 Count 类的 __call__ 方法,因此 num_calls 的值会被持续地累加。
换句话说,num_calls 是 Count 类的实例变量,而不是 example 函数的实例变量,因此其值可以被多次调用 example 函数所共享和修改。
装饰器的嵌套
Python支持多个装饰器
@decorator1
@decorator2
@decorator3
def func():
...
多个装饰器执行顺序是从里到外,decorator1(decorator2(decorator3(func)))
import functools
def my_decorator1(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print('装饰器1')
func(*args, **kwargs)
return wrapper
def my_decorator2(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print('装饰器2')
func(*args, **kwargs)
return wrapper
@my_decorator1
@my_decorator2
def greet(message):
print(message)
greet('hello python')
'''
装饰器1
装饰器2
hello python
'''
会先执行 my_decorator2 函数,返回一个 wrapper 函数作为装饰器;接着,再将 wrapper 函数作为参数传入 my_decorator1 函数中,生成一个新的 wrapper 函数,该函数在调用 greet 函数之前会打印 "装饰器1",然后再调用 my_decorator2 中的 wrapper 函数,打印 "装饰器2",最后才会执行 greet 函数
后续
到这里了解了装饰器,后续我们将实战,看如何使用装饰器解决响应复杂数据的问题。