大家好,欢迎来到IT知识分享网。
文章目录
前言
本篇文章,贯穿始终的示例是:为了不改变原函数的基础上,为其加上开始和终止的时间戳。
import time def func(): # 想在此处加上 print(time.strftime('%H:%M:%S', time.localtime()), '开始运行程序') print('程序主体正在运行中……') time.sleep(5) # 想在此处加上 print(time.strftime('%H:%M:%S', time.localtime()), '程序运行结束') func()
一、什么是装饰器
1.1 装饰器的概念及特点
- 装饰器不能修改被装饰函数的源码
- 装饰器不能修改被装饰函数的调用方式
二、高阶函数
构造函数的第一步是构造一个高阶函数。
2.1 什么是高阶函数
符合下列条件之一的函数就是高阶函数:
- 接受函数名作为形参
- 返回值中包含函数名
也就是说,一般函数是处理变量,返回变量值的。而高阶函数是处理函数,返回函数的。
2.2 变量与函数
上一节介绍什么是高阶函数时我们多次提到了函数名,那我们先探讨一下什么是函数名,什么是变量,他们有什么关系。
我们所说的python中的变量名与函数名,其实就是我们电脑中的一串内存地址,我们调用变量和函数的时候都是对其对应内存地址内的数据的调用。
>>> a = 5 >>> b = a # 使得b指向a所存储数据的内存地址,也就是把5这个数据所在的内存地址赋值给b >>> # 输出a, b的值 >>> print('a的值为:', a) a的值为: 5 >>> print('b的值为:', b) b的值为: 5 >>> # 输出a, b的内存地址,应该是相同的 >>> print('a的内存地址为:', id(a)) a的内存地址为: 96 >>> print('b的内存地址为:', id(b)) b的内存地址为: 96 >>>
函数也是一样的,可以参考Python —— 函数或类后面括号的作用
def func(arg): print(f'函数名为{
arg}') b = func # 使得b指向func函数所存在的内存地址,也就是把函数func所在的内存地址赋值给b # 执行func, b函数 func('func') b('b') # 输出func, b的内存地址,应该是相同的 print('a的内存地址为:', id(func)) print('b的内存地址为:', id(b)) """ 输出如下: 函数名为func 函数名为b a的内存地址为: 40 b的内存地址为: 40 """
2.3 高阶函数的使用及意义
我们先以前言中提到的目的为例:
import time def func(): # 想在此处加上 print(time.strftime('%H:%M:%S', time.localtime()), '开始运行程序') print('程序主体正在运行中……') time.sleep(5) # 想在此处加上 print(time.strftime('%H:%M:%S', time.localtime()), '程序运行结束') def higher_order_func(input_func): # 这里给高阶函数设置了形参,形参为一个函数名 print(time.strftime('%H:%M:%S', time.localtime()), '开始运行程序') return input_func # 这里返回了一个函数名 print(time.strftime('%H:%M:%S', time.localtime()), '程序运行结束') # 1. 这里我们将函数名func作为形参传入 # 2. 执行的higher_order_func函数并在中途返回了input_func # 3. 相当于我们又重新拿到了作为实参传入的func # 4. 我们再去执行他func() func = higher_order_func(func) func()
输出如下:
10:33:09 开始运行程序 程序主体正在运行中……
- 接受函数名作为形参(这样可以不改变被装饰代码的前提下增加功能)
- 返回值中包含函数名(不改变被装饰函数的调用方式)
但是细心的人就会从上面代码的执行结果发现两个问题:
- 开始运行程序的打印信息不是在执行
func()
函数的时候打印出来的,而是在func = higher_order_func(func)
时就已经打印了。如果两行代码中间有其他代码执行,那么这个时间就是不准的。 - 上述代码中程序运行结束没有打印出来
这里我们就需要用到嵌套函数来解决这个问题。
三、嵌套函数
2.1 什么是嵌套函数
嵌套函数,顾名思义,就是函数里面套了一个函数,形式如下:
def outer_func(): def inner_func(): pass inner_func() return inner_func
上面的例子中就是outer_func()里面又定义了inner_func()。我们可以在outer_func()内部调用它使用或者return后在外部调用他使用。这是后话我们后面再说
2.2 嵌套函数的意义
现在我们来解决我们上一章节留下来的问题。代码示例如下:
import time def func(): # 想在此处加上 print(time.strftime('%H:%M:%S', time.localtime()), '开始运行程序') print('程序主体正在运行中……') time.sleep(5) # 想在此处加上 print(time.strftime('%H:%M:%S', time.localtime()), '程序运行结束') def higher_order_func(input_func): # 这里给高阶函数设置了形参,形参为一个函数名 def add_info(): print(time.strftime('%H:%M:%S', time.localtime()), '开始运行程序') input_func() print(time.strftime('%H:%M:%S', time.localtime()), '程序运行结束') return add_info # 这里返回了higher_order_func中的子函数add_info,也就是返回了add_info的内存地址 # 1. 这里我们将函数名func作为形参传入 # 2. higher_order_func的返回值为add_info的函数名, # 3. 所以higher_order_func(func)相当于执行了higher_order_func函数并得到了返回值add_info, 得到了add_info函数 # 4. 那么higher_order_func(func)()就相当于add_info(), 也就是说我们执行了add_info() func = higher_order_func(func) func() # 这里实际相当于执行了higher_order_func(func)()
输出如下:
13:16:30 开始运行程序 程序主体正在运行中…… 13:16:35 程序运行结束
- 封装想要添加的功能代码
- 调用作为参数传入的函数名
- 返回嵌套函数的函数名
四、装饰器类型
4.1 普通装饰器
其实根据上述两个章节的介绍我们已经完成的简单的装饰器,就是高阶函数+嵌套函数 = 装饰器函数。但是Python给了我们一种更便捷、规范的方式来使用它。
我们认为上述代码中下述两行比较多余,我还得对func = higher_order_func(func)
赋值一下使用。
func = higher_order_func(func) func() # 这里实际相当于执行了higher_order_func(func)()
所以我们用@decorator
来替换上述赋值方式。具体代码如下:
import time def higher_order_func(input_func): def add_info(): print(time.strftime('%H:%M:%S', time.localtime()), '开始运行程序') input_func() print(time.strftime('%H:%M:%S', time.localtime()), '程序运行结束') return add_info @higher_order_func # 此处为Python语法糖,就相当于func函数名作为参数传给了名为higher_order_func的装饰器 def func(): # 想在此处加上 print(time.strftime('%H:%M:%S', time.localtime()), '开始运行程序') print('程序主体正在运行中……') time.sleep(5) # 想在此处加上 print(time.strftime('%H:%M:%S', time.localtime()), '程序运行结束') func()
输出如下
13:14:55 开始运行程序 程序主体正在运行中…… 13:15:00 程序运行结束
4.2 被装饰函数带参数
我们有些时候被装饰函数是要带参数的例如def func(name)
这样我们就需要对上述代码做出改变:
import time def higher_order_func(input_func): def add_info(*args, kwargs): # 可以传入任意形式的参数 print(time.strftime('%H:%M:%S', time.localtime()), '开始运行程序') input_func(*args, kwargs) # 将参数解包再传给input_func print(time.strftime('%H:%M:%S', time.localtime()), '程序运行结束') return add_info @higher_order_func # 此处为Python语法糖,就相当于func函数名作为参数传给了名为higher_order_func的装饰器 def func(name): # 想在此处加上 print(time.strftime('%H:%M:%S', time.localtime()), '开始运行程序') print('程序主体正在运行中……') print(f'传入参数为{
name}') time.sleep(5) # 想在此处加上 print(time.strftime('%H:%M:%S', time.localtime()), '程序运行结束') func('Tom') # 传入参数'Tom',由于实际相当于在执行add_info(),那么参数实际传入给了add_info()函数
输出如下:
13:35:27 开始运行程序 程序主体正在运行中…… 传入参数为Tom 13:35:32 程序运行结束
我们再详细的对其解读一下,其中我们用到了(*args, kwargs)
写法,我们可以参考Python ——*号的常用用法来学习。如果不想看就记住这种固定的搭配。
4.3 装饰器本身带参数
import time def higher_order_func(enable_start_info=True, enable_stop_info=True): def receive_func_name(input_func): def add_info(*args, kwargs): if enable_start_info: # 使用了装饰器传进来的参数 print(time.strftime('%H:%M:%S', time.localtime()), '开始运行程序') input_func(*args, kwargs) if enable_stop_info: # 使用了装饰器传进来的参数 print(time.strftime('%H:%M:%S', time.localtime()), '程序运行结束') return add_info return receive_func_name @higher_order_func(enable_start_info=False, enable_stop_info=True) # 此处对其传参启用具体功能 def func(name): # 想在此处加上 print(time.strftime('%H:%M:%S', time.localtime()), '开始运行程序') print('程序主体正在运行中……') print(f'传入参数为{
name}') time.sleep(5) # 想在此处加上 print(time.strftime('%H:%M:%S', time.localtime()), '程序运行结束') func('Tom') # 传入参数'Tom',由于实际相当于在执行add_info(),那么参数实际传入给了add_info()函数
输出:
程序主体正在运行中…… 传入参数为Tom 14:06:21 程序运行结束
4.4 被装饰函数带返回值
如果我们func函数有返回值,我们可以直接复制给一个变量,在add_info函数中返回。
import time def higher_order_func(enable_start_info=True, enable_stop_info=True): def receive_func_name(input_func): def add_info(*args, kwargs): if enable_start_info: # 使用了装饰器传进来的参数 print(time.strftime('%H:%M:%S', time.localtime()), '开始运行程序') input_func_return_value = input_func(*args, kwargs) if enable_stop_info: # 使用了装饰器传进来的参数 print(time.strftime('%H:%M:%S', time.localtime()), '程序运行结束') return input_func_return_value return add_info return receive_func_name @higher_order_func(enable_start_info=False, enable_stop_info=True) # 此处对其传参启用具体功能 def func(name): # 想在此处加上 print(time.strftime('%H:%M:%S', time.localtime()), '开始运行程序') print('程序主体正在运行中……') print(f'传入参数为{
name}') time.sleep(5) return 'func_return_value' # 想在此处加上 print(time.strftime('%H:%M:%S', time.localtime()), '程序运行结束') print(func('Tom')) # 由于实际相当于在执行add_info(),那么我们打印的就是add_info的返回值,上面我们把input_func的返回值传递给了add_info的返回值
输出如下:
程序主体正在运行中…… 传入参数为Tom 14:23:09 程序运行结束 func_return_value
参考文章
- Python开发编程高级进阶教程
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/140349.html