闭包和装饰器
闭包
定义:在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们把这个使用外部函数变量的内部函数称为闭包。
-
闭包的条件:
- 函数嵌套
- 内部函数使用外部函数的变量或者参数
- 外部函数返回内部函数的引用(即内部函数的内存地址)
-
简单的闭包示例代码:
def func_out(): num1 = 10 def func_inner(num2): result = num1 + num2 print(result) return func_inner inner = func_out() inner(50)
-
闭包的作用:
- 闭包可以保存外部函数内的变量,不会随着外部函数调用完而销毁
闭包的缺点:由于闭包引用了外部函数的变量,则外部函数的变量没有及时释放,消耗内存。
-
闭包的应用场景:
- 闭包是在不改变原有函数的调用方法和代码的基础上,对原有的功能进行增加
- 闭包大大的增加了代码的重用性,一个写好的函数可以供多人调用,而且还不会更改代码的结构
-
内部函数如何修改外部函数的变量
方法:使用nonlocal
示例代码:
def fun_out(): num = 10 def funn_inner(): nonlocal num num = 100 print(num) print(num) funn_inner() print(num) return funn_inner inner = fun_out() inner()
装饰器
定义:就是给已有函数增加额外功能的函数,它本质上就是一个闭包函数。
-
装饰器的特点:
- 不修改已有函数的的源代码
- 不修改已有函数的调用方式
- 给已有的函数增加额外的功能
-
装饰器的示例代码:
# 写个闭包 def fun(func): def inner(): print("登录") func() print("评论成功") return inner # 被装饰的函数 @fun # comment = fun(comment) 就相当于@fun def comment(): print("发表评论") # comment = fun(comment) comment() # 装饰器和闭包的区别: 1.装饰器是一种闭包 # 2.装饰器的外部函数的参数是被装饰的函数 # 3.其实就是把内部函数赋值给与被装饰函数同名的函数名,然后正常调用被装饰的函数
示例说明:
- 闭包函数有且只有一个参数,必须是函数类型,这样定义的函数才是装饰器。
- 写代码要遵循开放封闭原则,它规定已经实现的功能代码不允许被修改,但可以被扩展。
- @fun语法糖是另一种装饰器的写法,和传统的写法是一样的。以后我们经常用到的也会是语法糖的格式
-
装饰器的使用场景:
-
函数执行时间的统计
import time def make_time(fun): def inner(): start = time.time() fun() end = time.time() print(end - start) return inner @make_time def test(): for i in range(10000000): print(i) test()
输出日志文件
-
-
装饰带有参数的函数
-
示例代码:
def log(fun): def add_lig(num1, num2): # 因为这里相当于用inner来替代test,所以test有几个参数,inner就应该有几个参数 print("这是一个加法运算") fun(num1, num2) return add_lig @log def test(num1, num2): result = num1 + num2 print(result) test(10, 20) # 结论:内部函数的参数必须与被装饰的函数的参数一致,也要与内部函数调用的被装饰的函数的参数一致
-
-
装饰带有返回值的函数
-
示例代码:
def log(fun): def add_lig(num1, num2): # 因为这里相当于用inner来替代test,所以test有几个参数,inner就应该有几个参数 print("这是一个加法运算") return fun(num1, num2) return add_lig @log def test(num1, num2): result = num1 + num2 return result rs = test(10, 20) print(rs) # 结论:如果被装饰的函数有返回值,那么内部函数中就必须有返回值.
-
-
通用装饰器
通用装饰器就是被装饰函数是可以传递任意参数的函数,
-
示例代码:
def log(fun): def inner(num, *args, **kwargs): print("正在计算中") return fun(num,*args, **kwargs) return inner def test(num, *args, **kwargs): result = num for num in args: result += num for num in kwargs.values(): result += num return result result = test(10, 20, 30, a=20, b=30) print(result) # 总结:内部函数,被装饰函数,内部函数调用的函数 他们的参数必须一模一样
-
多装饰器的使用:
多装饰器就是多个装饰器同时装饰一个函数
-
代码示例:
# 使用div标签把内容包裹起来 def make_div(fun): print("--------") def inner(): result = "<div>" + fun() + "</div>" return result return inner # 使用p标签把内容包裹起来 def make_p(fun): print("+++++") def inner(): result = "<p>" + fun() + "</p>" return result return inner @make_div @make_p def content(): return "这是多装饰器的使用代码" rs = content() print(rs) # 总结: 多重装饰器先使用靠近被装饰函数的装饰器,类似于穿衣服,从下到上,从里到外
代码说明:当你调用content函数的时候,函数会先执行离它最近的那个装饰器make_P里面的代码,也就是U先打印"--------",然后内容变成了<p>这是多装饰器的使用代码</p>,然后会执行make_div里面的代码,打印出+++++,最后打印<div><p>这是多装饰器的使用代码</p></div>。
-
由前面的学习内容可以知道装饰器的外部函数能接受一个类型为函数的参数。即引用被装饰的函数。
那么装饰器的外部函数能不能直接传递其他的参数呢?
答案是:不能。
代码如下:
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 log(n): def test(fun): def inner(num1, num2): if n == "+": print("正在计算加法。") return fun(num1, num2) if n == "-": print("正在计算减法") return fun(num1, num2) return inner return test def sum_num(num1,num2): result = num1 + num2 return result test = log("+") sum_num = test(sum_num) @log("-") def sub_num(num1,num2): result = num1 - num2 return result rs = sum_num(20, 10) print(rs) rs1 = sub_num(20, 10) print(rs1)
这样就能在装饰器里面传递其他的参数了。
注意点:
- 调用装饰器后@后要调用最外部的那个函数,不再是之前的那个函数了。
- 一定要返回之前的那个装饰器函数的引用。
今天的分享就到这里了,如果哪里出错了,还请在评论区指正出来,我会积极改正。并且欢迎探讨python有关的知识。