python中普通函数到装饰器函数

python中普通函数到装饰器函数

这里我们不会介绍python中函数的基本使用,我们只介绍两种普通的函数:

  1. 函数的入参是函数
  2. 函数的返回值是函数

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

在这个例子中:

  1. 先定义了两个简单的函数:doubletriple, 它们的入参都是简单的int变量,返回值也是int
  2. 然后定义了一个主角函数:calc_number, 函数的入参是一个函数与普通的int变量,返回值为空
  3. 在主角函数calc_number中,我们调用了第一个入参func(), 并将第二个入参x作为参数传递给第一个入参。
  4. 最后是两次调用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

说明:

  1. 为了构造一个返回值为函数的情况,我们在函数get_multiple_func的内部定义了一个新的函数multiple, 然后在函数的最后将内部函数multiple返回
  2. 外部函数get_multiple_func与内部函数multiple各接收一个入参,最后内部函数用入参进行计算并返回
  3. 输出第1行:证明直接调用外层函数1次,返回的值的类型是函数类型
  4. 输出第2行:证明直接调用外层函数1次,返回的值是一个函数对象
  5. 输出的后面两行:证明把外层函数对象的返回值(也还是一个函数)再一次做调用,最终由内部函数返回具体的数值

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

说明:

  1. 代码中的timeit 就是一个非常典型的函数装饰器,用于打印被装饰的函数的执行时间
  2. 外层函数timeit 接收1个函数作为入参(func),用于捕获需要装饰的对象(通常是一个函数)
  3. 内层函数wrapper 接收1个或多个普通变量作为入参(这里只用了一个入参arg),用于捕获需要装饰的对象的函数本身的入参(可以是1个或者多个位置参数,也可能是关键字参数)
  4. 内层函数wrapper 中的逻辑是:
    1. 先计时
    2. 然后调用被装饰的对象(即本例中的func),用ret捕获返回值。
    3. 再打印函数的执行时间
    4. 最后将被装饰的对象的返回值(即本例中的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
这是一个非常常见的捕获不定长位置参数不定长关键字参数的方式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值