python中普通函数到装饰器函数
这里我们不会介绍python中函数的基本使用,我们只介绍两种普通的函数:
- 函数的入参是函数
- 函数的返回值是函数
1、 函数的入参是函数
在python中,函数的入参不止是不同的变量(int, float, list), 也可以是一个或者多个函数。
1.1 最简单的例子
先看一个例子:
from typing import Callable
def double(x: int) -> int:
return x * 2
def triple(x: int) -> int:
return x * 3
def calc_number(func: Callable, x: int) -> None:
print(func(x))
calc_number(double, 3)
calc_number(triple, 3)
这个例子中我增加了类型注解,让看代码的人与后面维护代码的人更清楚代码的参数与返回值的类型,不加类型注解完全也可以工作
输出的结果:
6
9
在这个例子中:
- 先定义了两个简单的函数:
double
与triple
, 它们的入参都是简单的int变量,返回值也是int - 然后定义了一个主角函数:
calc_number
, 函数的入参是一个函数与普通的int变量,返回值为空 - 在主角函数
calc_number
中,我们调用了第一个入参func()
, 并将第二个入参x
作为参数传递给第一个入参。 - 最后是两次调用
calc_number
函数
这里的逻辑很简单, 就没必要解释什么了。
2、 函数的返回值是函数
2.1 最简单的例子
示例代码如下:
def get_multiple_func(n: int):
def multiple(x: int):
return n * x
return multiple
print(f"{type(get_multiple_func(4)) = }")
print(get_multiple_func(4))
print(get_multiple_func(2)(3))
print(get_multiple_func(3)(3))
输出的内容:
type(get_multiple_func(4)) = <class 'function'>
<function get_multiple_func.<locals>.multiple at 0x7f69270f64c0>
6
9
说明:
- 为了构造一个返回值为函数的情况,我们在函数
get_multiple_func
的内部定义了一个新的函数multiple
, 然后在函数的最后将内部函数multiple
返回 - 外部函数
get_multiple_func
与内部函数multiple
各接收一个入参,最后内部函数用入参进行计算并返回 - 输出第1行:证明直接调用外层函数1次,返回的值的类型是函数类型
- 输出第2行:证明直接调用外层函数1次,返回的值是一个函数对象
- 输出的后面两行:证明把外层函数对象的返回值(也还是一个函数)再一次做调用,最终由内部函数返回具体的数值
2.2 上面例子的等价形式
这里展示了上面例子的基本等价形式(对于打印过程稍作修改), 示例代码如下:
def get_multiple_func(n: int):
def multiple(x: int):
return n * x
return multiple
double = get_multiple_func(2)
triple = get_multiple_func(3)
print(f"{type(double) = }")
print(double)
print(double(3))
print(triple(3))
返回结果:
type(double) = <class 'function'>
<function get_multiple_func.<locals>.multiple at 0x7f572b95b4c0>
6
9
这种写法与上面的例子本质上没有任何区别,只是将返回的内部函数multiple
做了重新命名,以便后面的函数调用。
2.3 斐波那契数列(Fibonacci sequence)
有趣的斐波那契数列(Fibonacci sequence),示例代码如下:
def fib_recur(n):
if n <= 1:
return n
else:
return fib_recur(n - 1) + fib_recur(n - 2)
for i in range(1, 10):
print(fib_recur(i))
输出结果:
1
1
2
3
5
8
13
21
34
斐波那契数列姑且算是返回值是函数的一种特例吧,因为它在返回值恰好是函数本身的简单运算。
切记: 它的最终返回值是简单的整数值而不是一个可调用对象
3、 简单的装饰器
当你看懂了上面的内容,就很容易理解装饰器了。
3.1 简单的装饰器举例
示例代码如下:
import time
from typing import Callable
def timeit(func: Callable):
def wrapper(arg: int):
start = time.time()
ret = func(arg)
print(time.time() - start)
return ret
return wrapper
@timeit
def my_func(x: int):
time.sleep(x)
my_func(1)
输出结果:
1.0004031658172607
说明:
- 代码中的
timeit
就是一个非常典型的函数装饰器,用于打印被装饰的函数的执行时间 - 外层函数
timeit
接收1个函数作为入参(func
),用于捕获需要装饰的对象(通常是一个函数) - 内层函数
wrapper
接收1个或多个普通变量作为入参(这里只用了一个入参arg
),用于捕获需要装饰的对象的函数本身的入参(可以是1个或者多个位置参数,也可能是关键字参数) - 内层函数
wrapper
中的逻辑是:- 先计时
- 然后调用被装饰的对象(即本例中的
func
),用ret
捕获返回值。 - 再打印函数的执行时间
- 最后将被装饰的对象的返回值(即本例中的
ret
)返回
稍加改动,我们就写成了一个可以实际使用的装饰器:
import time
from typing import Callable
def timeit(func: Callable):
def wrapper(*args, **kwargs):
start = time.time()
ret = func(*args, **kwargs)
print(time.time() - start)
return ret
return wrapper
其实就是将上面例子中的1个入参
arg
,改为了*args, **kwargs
这是一个非常常见的捕获不定长位置参数与不定长关键字参数的方式