Python_闭包和装饰器
闭包的介绍
闭包的定义:
在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们把这个使用外部函数变量的内部函数称为闭包。
闭包的构成条件
通过闭包的定义,我们可以得知闭包的形成条件:
- 在函数嵌套(函数里面再定义函数)的前提下
- 内部函数使用了外部函数的变量(还包括外部函数的参数)
- 外部函数返回了内部函数
简单闭包的示例代码
# 定义一个外部函数 def func_out(num1): # 定义一个内部函数 def func_inner(num2): # 内部函数使用了外部函数的变量(num1) result = num1 + num2 print("结果是:", result) # 外部函数返回了内部函数,这里返回的内部函数就是闭包 return func_inner # 创建闭包实例 f = func_out(1) # 执行闭包 f(2) f(3)
运行结果:
结果是: 3
结果是: 4
闭包执行结果的说明:
通过上面的输出结果可以看出闭包保存了外部函数内的变量num1,每次执行闭包都是在num1 = 1 基础上进行计算。
闭包的作用
- 闭包可以保存外部函数内的变量,不会随着外部函数调用完而销毁。
注意点:
- 由于闭包引用了外部函数的变量,则外部函数的变量没有及时释放,消耗内存。
小结
- 当返回的内部函数使用了外部函数的变量就形成了闭包
- 闭包可以对外部函数的变量进行保存
-
实现闭包的标准格式:
# 外部函数 def test1(a): b = 10 # 内部函数 def test2(): # 内部函数使用了外部函数的变量或者参数 print(a, b) # 返回内部函数, 这里返回的内部函数就是闭包实例 return test2
闭包的使用
需求: 根据配置信息使用闭包实现不同人的对话信息,例如对话:
张三: 到北京了吗?
李四: 已经到了,放心吧。
# 外部函数 def config_name(name): # 内部函数 def say_info(info): print(name + ": " + info) return say_info tom = config_name("Tom") tom("你好!") tom("你好, 在吗?") jerry = config_name("jerry") jerry("不在, 不和你玩!")
运行结果:
Tom: 你好!
Tom: 你好, 在吗?
jerry: 不在, 不和你玩!
修改闭包内使用的外部变量
# 定义一个外部函数 def func_out(num1): # 定义一个内部函数 def func_inner(num2): # 这里本意想要修改外部num1的值,实际上是在内部函数定义了一个局部变量num1 num1 = 10 # 内部函数使用了外部函数的变量(num1) result = num1 + num2 print("结果是:", result) print(num1) func_inner(1) print(num1) # 外部函数返回了内部函数,这里返回的内部函数就是闭包 return func_inner # 创建闭包实例 f = func_out(1) # 执行闭包 f(2)
修改闭包内使用的外部变量的正确示例:
# 定义一个外部函数 def func_out(num1): # 定义一个内部函数 def func_inner(num2): # 这里本意想要修改外部num1的值,实际上是在内部函数定义了一个局部变量num1 nonlocal num1 # 告诉解释器,此处使用的是 外部变量a # 修改外部变量num1 num1 = 10 # 内部函数使用了外部函数的变量(num1) result = num1 + num2 print("结果是:", result) print(num1) func_inner(1) print(num1) # 外部函数返回了内部函数,这里返回的内部函数就是闭包 return func_inner # 创建闭包实例 f = func_out(1) # 执行闭包 f(2)
装饰器的定义
就是给已有函数增加额外功能的函数,它本质上就是一个闭包函数。
装饰器的功能特点:
- 不修改已有函数的源代码
- 不修改已有函数的调用方式
- 给已有函数增加额外的功能
装饰器的示例代码
# 添加一个登录验证的功能 def check(fn): def inner(): print("请先登录....") fn() return inner def comment(): print("发表评论") # 使用装饰器来装饰函数 comment = check(comment) comment()
代码说明:
- 闭包函数有且只有一个参数,必须是函数类型,这样定义的函数才是装饰器。
- 写代码要遵循开放封闭原则,它规定已经实现的功能代码不允许被修改,但可以被扩展。
执行结果:
请先登录....
发表评论
装饰器的语法糖写法
如果有多个函数都需要添加登录验证的功能,每次都需要编写func = check(func)这样代码对已有函数进行装饰,这种做法还是比较麻烦。
Python给提供了一个装饰函数更加简单的写法,那就是语法糖,语法糖的书写格式是: @装饰器名字,通过语法糖的方式也可以完成对已有函数的装饰
# 添加一个登录验证的功能 def check(fn): print("装饰器函数执行了") def inner(): print("请先登录....") fn() return inner # 使用语法糖方式来装饰函数 @check def comment(): print("发表评论") comment()
说明:
- @check 等价于 comment = check(comment)
- 装饰器的执行时间是加载模块时立即执行。
执行结果:
请先登录....
发表评论
装饰器的使用
- 函数执行时间的统计
- 输出日志信息
装饰器实现已有函数执行时间的统计
import time # 装饰器函数 def get_time(func): def inner(): begin = time.time() func() end = time.time() print("函数执行花费%f" % (end-begin)) return inner @get_time def func1(): for i in range(100000): print(i) func1()
执行结果:
... 99995 99996 99997 99998 99999 函数执行花费0.329066
装饰带有参数的函数
# 添加输出日志的功能 def logging(fn): def inner(num1, num2): print("--正在努力计算--") fn(num1, num2) return inner # 使用装饰器装饰函数 @logging def sum_num(a, b): result = a + b print(result) sum_num(1, 2)
运行结果:
--正在努力计算-- 3
装饰带有返回值的函数
# 添加输出日志的功能 def logging(fn): def inner(num1, num2): print("--正在努力计算--") result = fn(num1, num2) return result return inner # 使用装饰器装饰函数 @logging def sum_num(a, b): result = a + b return result result = sum_num(1, 2) print(result)
运行结果:
--正在努力计算-- 3
装饰带有不定长参数的函数
# 添加输出日志的功能 def logging(fn): def inner(*args, **kwargs): print("--正在努力计算--") fn(*args, **kwargs) return inner # 使用语法糖装饰函数 @logging def sum_num(*args, **kwargs): result = 0 for value in args: result += value for value in kwargs.values(): result += value print(result) sum_num(1, 2, a=10)
运行结果:
--正在努力计算-- 13
通用装饰器
# 添加输出日志的功能 def logging(fn): def inner(*args, **kwargs): print("--正在努力计算--") result = fn(*args, **kwargs) return result return inner # 使用语法糖装饰函数 @logging def sum_num(*args, **kwargs): result = 0 for value in args: result += value for value in kwargs.values(): result += value return result @logging def subtraction(a, b): result = a - b print(result) result = sum_num(1, 2, a=10) print(result) subtraction(4, 2)
运行结果:
--正在努力计算-- 13 --正在努力计算-- 2
多个装饰器的使用示例代码
def make_div(func): """对被装饰的函数的返回值 div标签""" def inner(): return "<div>" + func() + "</div>" return inner def make_p(func): """对被装饰的函数的返回值 p标签""" def inner(): return "<p>" + func() + "</p>" return inner # 装饰过程: 1 content = make_p(content) 2 content = make_div(content) # content = make_div(make_p(content)) @make_div @make_p def content(): return "人生苦短" result = content() print(result)
带有参数的装饰器介绍
错误写法:
def decorator(fn, flag): def inner(num1, num2): if flag == "+": print("--正在努力加法计算--") elif flag == "-": print("--正在努力减法计算--") result = fn(num1, num2) return result return inner @decorator('+') def add(a, b): result = a + b return result result = add(1, 3) print(result)
正确写法:
在装饰器外面再包裹上一个函数,让最外面的函数接收参数,返回的是装饰器,因为@符号后面必须是装饰器实例。
# 添加输出日志的功能 def logging(flag): def decorator(fn): def inner(num1, num2): if flag == "+": print("--正在努力加法计算--") elif flag == "-": print("--正在努力减法计算--") result = fn(num1, num2) return result return inner # 返回装饰器 return decorator # 使用装饰器装饰函数 @logging("+") def add(a, b): result = a + b return result @logging("-") def sub(a, b): result = a - b return result result = add(1, 2) print(result) result = sub(1, 2) print(result)
装饰器的执行顺序
import time
def log(level="info"):
print ("log")
def decorater(func): print ("wrapper start") def wrapper(*args,**kwargs): print( "inner_wrapper start") func(*args, **kwargs) print ("inner_wrapper end") print ("wrapper end") return wrapper
print ("end")
return decorater