闭包
- 当一个函数在内部定义函数,并且内部的函数应用外部函数的参数或者局部变量,当内部函数被当做返回值时,相关参数和变量保存在返回的函数中,这种结果叫闭包。
# 闭包结构,myF5用到了myF4的参数args
def myF4(*args):
def myF5():
rst = 0
for n in args:
rst += n
return rst
return myF5
# 常见的闭包错误
def count():
# 定义列表,列表里存放的是定义的函数
fs = []
for i in range(1,4):
# 定义了一个函数f
# f是一个闭包结构
def f():
return i*i
fs.append(f)
return fs
f1,f2,f3 = count()
print(f1())
print(f2())
print(f3())
# 此时的输出会是9,9,9,并不是我们想要的1,4,9
- 造成上述状况的原因是,返回函数引用变量i,i并非立即执行,而是等到三个函数都返回的时候才统一使用,此时i已经变成了3,最终调用的时候,都是返回3*3
- 此问题描述成: 返回闭包时,返回函数不能引用任何循环变量
- 解决方案: 用函数的参数绑定循环变量的当前值,无论该循环变量以后如何改变,已经绑定的函数参数值不再改变
def count():
# 定义列表,列表里存放的是定义的函数
fs = []
for i in range(1,4):
# 定义了一个函数f
# f是一个闭包结构
def f(a=i):
return a*a
fs.append(f)
return fs
装饰器
装饰器的形成
假设现在有一个hello函数
def hello():
print("Hello world")
hello()
现在有新的需求需要对hello功能进行扩展,每次打印hello之前打印当前系统时间,而实现这个功能又不能改动现有代码
import time
def printTime(f):
def inner(*args, **kwargs):
print("Time:",time.ctime())
return f(*args, **kwargs)
return inner
# 上面定义了装饰器,使用的时候需要用到@
@printTime
def hello():
print("Hello world")
hello()
>>> Time: Sat Feb 8 17:39:47 2020
Hello world
装饰器的作用
- 不想修改函数的调用方式,但是想在原来的函数前后添加功能
- 装饰器的好处是,一旦定义,则可以装饰任意函数
- 一旦被其装饰,则把装饰器的功能直接添加到定义函数的功能上
原则: 开放封闭原则
- 开放:对扩展是开放的
- 封闭:对修改是封闭的
可以看到装饰器一定是闭包,但闭包不一定是装饰器。
装饰器的固定模式
def wrapper(f): # f是被装饰的函数
def inner(*args,**kwargs):
# 添加 被装饰函数前要添加的功能
ret = f(*args,**kwargs)
# 被装饰函数后要添加的功能
return ret
return inner
@wrapper
def f(): # 被装饰的函数
pass
f()
# 添加装饰器后执行f() 相当于是
# 没添加装饰器时
#f = wrapper(f)
#f()
打印被装饰函数的函数名
如果函数没有被装饰,要想打印函数名的话,执行以下命令。
def hello():
print("Hello world")
print(hello.__name__)
>>> hello
当函数被装饰后,如果直接用上面的方法,打印出的名字会改变。
import time
def printTime(f):
def inner(*args, **kwargs):
print("Time:",time.ctime())
return f(*args, **kwargs)
return inner
@printTime
def hello():
print("Hello world")
print(hello.__name__)
>>> inner
对此,我们需要从functools引入wraps包
import time
from functools import wraps
def printTime(f):
@wraps(f) # 用wraps装饰被装饰的函数f
def wrapper(*args, **kwargs):
print("Time:",time.ctime())
return f(*args, **kwargs)
return wrapper
@printTime
def hello():
print("Hello world")
print(hello.__name__)
>>> hello
带参数的装饰器
假如我们之前已经对多个函数进行装饰,现在想取消掉对每个函数的装饰,如果一个一个加注释,当函数数量过大的时候,太费时间。我们可以通过flag标志,判断函数是否需要装饰
import time
flag = True
def outer(flag):
def printTime(f):
def wrapper(*args, **kwargs):
if flag:
print("Time:",time.ctime())
return f(*args, **kwargs)
else:
return f(*args, **kwargs)
return wrapper
return printTime
@outer(flag) # 先调用outer(flag)返回printTime,最后相当于是@printTime
def hello1():
print("Hello world")
@outer(flag)
def hello2():
print("Hi")
hello1()
hello2()
多个装饰器装饰一个函数
def outer1(f):
def wrapper1(*args, **kwargs):
print("outer1, before func")
f(*args, **kwargs)
print("outer1, after func")
return wrapper1
def outer2(f):
def wrapper2(*args, **kwargs):
print("outer2, before func")
f(*args, **kwargs)
print("outer2, after func")
return wrapper2
@outer2
@outer1
def func():
print("In func")
func()
运行结果为
outer2, before func
outer1, before func
In func
outer1, after func
outer2, after func
解释下运行结果为什么如上所示
函数执行是从上往下,第一步先定义outer1,然后定义outer2,执行到@outer2时,因为紧挨着他的是@outer1 ,不是某个函数的定义,先执行@outer1 ,相当于是执行func = outer1(func),从而将wrapper1返回给func;再执行@outer2,相当于是func = outer2(wrapper1),进而将wrapper2返回给func;最后调用func()时相当于调用wrapper2()。
红色标记表示在调用func()前的执行顺序,蓝色标记表示调用后的执行顺序。
来源:CSDN
作者:kkklern
链接:https://blog.csdn.net/zll1130/article/details/104246546