Python装饰器本质上是对函数闭包的语法糖
函数闭包
把主要逻辑和辅助功能写在同一个函数里: 难以修改,容易出bug
通过辅助功能函数调用主要功能函数: 函数逻辑反了,难以封装成模块
函数闭包: 需要显式调用函数闭包
装饰器: 其实就是函数闭包,只是不用显式调用了而已
装饰器
装饰器本质上是对函数闭包的语法糖
装饰器在第一次调用被装饰的函数时调用闭包进行函数增强
一个通用的装饰器: 保留原函数的参数列表和返回值
一道面试题: 多个装饰器的执行顺序
一道思考题: 如何创建带参数的装饰器
装饰器是Python中的一个容易令人困惑的概念,但实际上,只要掌握两句口诀,就可以掌握装饰器,搞定那些令人困惑的面试题.
装饰器本质上是对函数闭包的语法糖.
装饰器在第一次调用被装饰的函数时调用闭包进行函数增强.
函数闭包
函数闭包本质上是一个函数,它的接收参数和返回值也都是函数,返回的函数本质上是对传入的参数进行增强之后的结果.
下面,我们从一个例子来引入函数闭包:
假设我们有一个主要需求(主要功能): 统计0~100之间的所有奇数,还有一个额外的需求(辅助功能):统计函数运行的时间,我们从各种不同的写法,来引入闭包.
把主要逻辑和辅助功能写在同一个函数里: 难以修改,容易出bug
假设我们不使用任何函数增强技术,将所有代码写入一个文件里,代码如下:
import time
def print_odds():
"""
输出0~100之间所有奇数,并统计函数执行时间
"""
start_time = time.clock() # 起始时间
# 查找并输出所有奇数
for i in range(100):
if i % 2 == 1:
print(i)
end_time = time.clock() # 结束时间
print("it takes {} s to find all the olds".format(end_time - start_time))
if __name__ == '__main__':
print_odds()
上述代码奇丑无比,它之所以丑,是因为把主要功能逻辑(输出奇数)和辅助功能(记录时间)耦合在一起了,这样会导致程序的可读性很差,且难以修改,容易出bug.
通过辅助功能函数调用主要功能函数: 函数逻辑反了,难以封装成模块
对上面的代码加以改进,既然要解耦合,就将主要功能逻辑(输出奇数)和辅助功能(记录时间)分到两个函数里就好了,通过函数调用(将主要功能逻辑print_odds
传入辅助函数count_time
,在辅助函数内部调用主要功能逻辑)来实现功能.
import time
def count_time(func):
"""
统计某个函数的运行时间
"""
start_time = time.clock() # 起始时间
func() # 执行函数
end_time = time.clock() # 结束时间
print("it takes {} s to find all the olds".format(end_time - start_time))
def print_odds():
"""
输出0~100之间所有奇数,并统计函数执行时间
"""
for i in range(100):
if i % 2 == 1:
print(i)
if __name__ == '__main__':
count_time(print_odds)
这个代码好在解耦合了,方便对主要功能逻辑和辅助功能逻辑分别进行修改.
上述程序可以正常执行,在语法上没有问题,但是在逻辑上是存在问题的,它违反了设计模式中的开闭原则: 在main
函数里调用了count_time
.模块的主要功能函数print_odds
应该是对使用