Python装饰器:从入门到精通
目录
一、装饰器的本质与定义
二、装饰器的雏形与语法糖
1. 装饰器的雏形
2. 装饰器的语法糖
三、装饰器的常见应用
1. 获取程序的执行时间
2. 处理带参数的函数
3. 处理有返回值的函数
四、通用版本的装饰器
五、装饰器的高级用法
1. 给装饰器传递参数
六、总结
在 Python 的进阶特性中,装饰器无疑是一颗璀璨的明珠。它能够在不改变函数源代码和调用方式的前提下,为函数增添新的功能,是代码复用和功能扩展的绝佳方案。
一、装饰器的本质与定义
装饰器的本质是一个闭包函数,这意味着它满足闭包的三个构成条件:有嵌套、有引用、有返回(返回的是函数的内存地址)。简单来说,装饰器就是这样一种工具:在不改变现有函数源代码以及函数调用方式的前提下,实现给函数增加额外的功能。
二、装饰器的雏形与语法糖
1. 装饰器的雏形
最初的装饰器实现,需要手动将被装饰的函数传入装饰器函数,并重新赋值。例如,要给评论和下载功能添加登录验证,可这样实现:
def check(fun):def inner():print('登录功能')fun()return innerdef comment():print('评论功能')
comment = check(comment)
comment()def download():print('下载功能')
download = check(download)
download()
这里的check函数就是一个装饰器雏形,它接收要装饰的函数fun作为参数,内部嵌套的inner函数实现了额外的登录功能,并调用了原函数fun,最后返回inner函数。
2. 装饰器的语法糖
为了简化装饰器的使用,Python 提供了@语法糖。使用@装饰器的形式,可直接将装饰器应用于函数,无需手动赋值。例如:
def check(fun):def inner():print('验证登录')fun()return inner@check
def comment():print('发表评论')
comment()
此时,调用comment(),实际上是调用了装饰器内部的inner函数,先执行登录验证,再执行原函数的功能。
三、装饰器的常见应用
1. 获取程序的执行时间
装饰器可以轻松实现获取函数执行时间的功能。通过在函数执行前后记录时间戳,计算差值即可得到执行时间:
import time
def get_time(fun):def inner():begin = time.time()fun()end = time.time()print(f'这个函数的执行时间:{end - begin}')return inner@get_time
def demo():for i in range(1000000):print(i)
demo()
当调用demo()时,会自动计算并打印该函数的执行时间。
2. 处理带参数的函数
对于带有参数的函数,装饰器的内部函数可以使用*args和**kwargs来接收不定长参数,确保原函数的参数能正确传递:
def logging(fun):def inner(*args, **kwargs):print('-- 日志信息:正在努力计算机 --')fun(*args, **kwargs)return inner@logging
def sum_num(*args, **kwargs):result = 0for i in args:result += ifor i in kwargs.values():result += iprint(result)print(sum_num())
这样,sum_num函数可以接收任意数量的位置参数和关键字参数,装饰器也能正常工作。
3. 处理有返回值的函数
如果被装饰的函数有返回值,装饰器的内部函数需要将原函数的返回值返回,以保证函数功能的完整性:
def logging(fun):def inner(*args, **kwargs):print('-- 日志信息:正在努力计算 --')return fun(*args, **kwargs)return inner@logging
def sub_num(a, b):result = a - breturn resultprint(sub_num(20, 10))
调用sub_num(20,10)时,装饰器会返回原函数的计算结果。
四、通用版本的装饰器
综合考虑参数和返回值的情况,通用版本的装饰器应具备以下特点:有嵌套、有引用、有返回、有不定长参数、有 return 返回值。其代码示例如下:
def logging(fn):def inner(*args, **kwargs):print('-- 正在努力计算 --')return fn(*args, **kwargs)return inner@logging
def sum_num1(a, b):result = a + breturn result@logging
def sum_num2(a, b, c):result = a + b + creturn result
这个通用装饰器可以适用于各种类型的函数,无论是带参数还是有返回值的情况。
五、装饰器的高级用法
1. 给装饰器传递参数
通过在装饰器外侧再添加一个函数,专门用于接收参数,可以实现根据传递的参数不同,让装饰器表现出不同的功能。例如:
def logging(flag):def decorator(fun):def inner(*args, **kwargs):if flag == '+':print('-- 日志信息:正在努力进行加法运算 --')elif flag == '-':print('-- 日志信息:正在努力进行减法运算 --')return fun(*args, **kwargs)return innerreturn decorator@logging('+')
def sum_num(a, b):result = a + breturn resultprint(sum_num(2, 3))@logging('-')
def sub_num(a, b):result = a - breturn resultprint(sub_num(5, 2))
这里的logging函数接收参数flag,根据flag的值,装饰器会打印不同的日志信息。
六、总结
装饰器是 Python 中一种非常强大且优雅的特性,它基于闭包实现,能够在不改变原有函数的情况下为其增添功能。从简单的功能扩展到复杂的参数传递和类实现,装饰器展现出了极大的灵活性和实用性。掌握装饰器的使用,能够让我们的代码更加简洁、高效和可维护。