闭包定义:在函数嵌套(函数内再定义函数)的前提下,内部函数使用了外部函数变量或参数,且外部函数返回了内部函数,则将这个使用了外部函数变量的内部函数称为闭包
闭包作用特点
- def outFunc(a): # 定义外部函数,a=5
- b = 2
- def inFunc(c): # 定义内部函数 c=1和3
- result = a + b + c # 内部函数使用了外部函数参数a和变量b
- print('结果为:', result)
- return inFunc # 外部函数返回内部函数,该内部函数即为闭包,是一个引用,该引用存入了out对象中,故执行out()时相当于运行了inFunc()函数
- out = outFunc(5) # 创建闭包实例,执行outFunc函数,传参给了a
- out(1) # 执行闭包inFunc函数,传参给了c
- out(3)
-
-
- 输出: # 由结果可知闭包保存了外部函数的参数a和变量b,每次执行闭包都是在a=5,b=2基础上计算
- 结果为: 8
- 结果为: 10
根据配置使用闭包实现多人之间的对话
- def configName(name):
- def sayInfo(info):
- print(name + ": " + info)
- return sayInfo
-
- xy = configName("XY")
- xy("你好,在不")
- xy("你好!在不在?")
-
- yx = configName("YX")
- yx("不在。")
-
- 输出:
- XY: 你好,在不
- XY: 你好!在不在?
- YX: 不在。!
使用nonlocal关键字完成,具体举例如下
- def outFunc(a): # a=1
- b = 2
- def inFunc(c): # c=8和3
- # a = 5 # 错误修改,本意要修改外部参数a的值,实际在内部函数定义了一个局部变量a
-
- # 正确修改
- nonlocal a # 告诉解释器,此处使用的是外部变量a
- a = 5 # 修改
- result = a + b + c
- print('结果为:', result)
- print('修改前的a值:', a) # a=1
- inFunc(8)
- print('修改后的a值:', a) # a=5
- return inFunc # 外部函数返回内部函数,该内部函数即闭包
- out = outFunc(1) # 创建闭包实例,执行outFunc函数,a=1
- out(3) # 执行闭包函数inFunc,c=3
-
- 输出:
- 修改前的a值: 1
- 结果为: 15
- 修改后的a值: 5
- 结果为: 10
装饰器定义:指为已有函数增加额外功能的函数,本质上就是一个闭包函数,闭包函数有且只有一个参数并为函数类型,规定已实现的功能代码不允许被修改,但可以被扩展
装饰器功能特点
- def outFunc(out): # 定义装饰器,out为被装饰的目标函数,只有一个参数,且为函数类型(下面调用了)
- def inFunc(): # 在内部函数中对已有函数进行装饰
- print('额外功能……')
- out() # 执行被装饰的目标函数,即执行调用ordinaryFunc函数
- return inFunc
-
- # def ordinaryFunc():
- # print('已有普通函数……')
- # ordinaryFunc = outFunc(ordinaryFunc) # 调用装饰器来装饰已有的ordinaryFunc函数,ordinaryFunc=inFunc
- # ordinaryFunc() # 执行inFunc函数
-
- # 语法糖写法,书写格式为:@装饰器名字,通过语法糖可以完成对已有函数的装饰
- @outFunc # 等价于ordinary = outFunc(ordinaryFunc)
- def ordinaryFunc(): # 已有函数不变
- print('已有普通函数……')
- ordinaryFunc() # 执行inFunc函数,调用方式不变
-
- 输出:
- 额外功能……
- 已有普通函数……
实例:装饰器实现统计已有函数执行时间,可知装饰器能够在不改变已有函数源代码及调用方式的前提下,对已有函数进行功能的扩展
- import time
- def getTime(func):
- def inFunc():
- begin = time.time()
- func()
- end = time.time()
- print('func()函数的执行时间为:%.5f' % (end-begin))
- return inFunc
- @getTime
- def ordinaryFunc():
- for i in range(100000):
- print('这是第%d条数据!' % i)
- ordinaryFunc()
-
- 输出:
- ……
- ……
- 这是第99998条数据!
- 这是第99999条数据!
- func()函数执行时间为:1.77821
1.装饰器带有参数的函数
- def outFunc(func):
- def inFunc(a, b): # 使用装饰器装饰已有函数时,内部函数类型和已有函数保持一致
- func(a, b)
- return inFunc
- @outFunc # addSum = outFunc(addSum),即addSum=inFunc
- def addSum(c, d):
- sum = c + d
- print('结果为:', sum)
- addSum(10, 3) # 调用执行inFunc
-
- 输出:
- 结果为: 13
2.装饰器带返回值的函数
- # 装饰器带返回值的函数
- def outFunc(func):
- def inFunc(a, b):
- result = func(a, b) # 调用执行sum()
- return result
- return inFunc
- @outFunc
- def sum(c, d):
- sum = c + d
- return sum
-
- sum2 = sum(10, 3) # 调用执行inFunc
- print('结果为:', sum2)
-
- 输出:
- 结果为: 13
3.装饰带不定长参数的函数
- # 装饰带不定长参数和返回值的函数
- def outFunc(func):
- def inFunc(*args, **kwargs): # 使用装饰器装饰已有函数时,内部函数类型和已有函数保持一致
- func(*args, **kwargs) # 执行addSum
- return inFunc
- @outFunc # addSum = outFunc(addSum),即addSum=inFunc
- def addSum(*args, **kwargs):
- sum = 0
- for v in args:
- sum += v
- for v in kwargs.values():
- sum += v
- print('结果为:', sum)
- addSum(1, 3, a=5, b=2, c=2) # 调用执行inFunc
-
- 输出:
- 结果为: 13
4.通用装饰器
- # 通用装饰器
- def outFunc(func):
- def inFunc(*args, **kwargs): # 使用装饰器装饰已有函数时,内部函数类型和已有函数保持一致
- result = func(*args, **kwargs) # 执行addSum
- return result
- return inFunc
- @outFunc # addSum = outFunc(addSum),即addSum=inFunc
- def addSum(*args, **kwargs):
- sum = 0
- for v in args:
- sum += v
- for v in kwargs.values():
- sum += v
- return sum
-
- @outFunc
- def subtraction(a,b):
- result1 = a - b
- print('a - b结果为:', result1)
- result2 = addSum(1, 3, a=5) # 调用执行inFunc,1、3为args元组,a=5为kwargs字典
- print('结果为:', result2)
- subtraction(9, 4) # 调用执行inFunc,9、4为args元组
-
- 输出:
- 结果为: 9
- a - b结果为: 5
装饰过程:离函数最近的装饰器先装饰,然后外面的装饰器再进行装饰,即由内到外的装饰
- def outFunc(func):
- def inFunc(*args, **kwargs): # 使用装饰器装饰已有函数时,内部函数类型和已有函数保持一致
- return "" + func() + ""
- return inFunc
- def outFunc2(func):
- def inFunc2(*args, **kwargs): # 使用装饰器装饰已有函数时,内部函数类型和已有函数保持一致
- return "
"
+ func() + "" - return inFunc2
-
- @outFunc
- @outFunc2
- def info():
- return "Hello World!"
- result = info() # 首先执行inFunc
- print('结果为:', result)
-
- 输出:
- 结果为:
Hello World!
单步调试执行过程如下

使用带参数的装饰器,需在装饰器外再包裹一个函数,该函数用来接收参数,返回是装饰器,因为 @符号需要配合装饰器实例使用
- def out(flag): # 在装饰器外包裹一个out函数,用来接收参数,返回装饰器,因为@后必须为装饰器实例
- def outFunc(func): # 装饰器只能接收一个参数,且只能是函数类型,故需在外部再加一个out函数
- def inFunc(c, d): # 使用装饰器装饰已有函数时,内部函数类型和已有函数保持一致
- if flag=="+":
- print("正在进行加法运算")
- elif flag=="-":
- print('正在进行减法运算')
- result = func(c, d)
- return result
- return inFunc
- return outFunc # 返回装饰器
-
- @out("+") # 使用装饰器装饰函数
- def add(a, b):
- sum = a + b
- return sum
-
- @out("-")
- def sub(a, b):
- result = a - b
- return result
-
- result1 = add(3, 5) # 执行inFunc,先进行加减判定,再执行func(),根据判定确定执行add()或sub()函数
- result2 = sub(5, 2)
- print('加法运算result1结果为:%d,减法运算result2结果为:%d' % (result1, result2))
-
- 输出:
- 正在进行加法运算
- 正在进行减法运算
- 加法运算result1结果为:8,减法运算result2结果为:3
- class decorate(object): # 类装饰器
- def __init__(self, func):
- self.func = func
- def __call__(self, *args, **kwargs): # 实现该方法,表示对象是一个可调用对象,能像调用函数一样进行调用
- print('添加装饰功能')
- self.func() # 执行info()方法
-
- @decorate # 等价于info = decorate(info),故需提供一个init方法,并多增加一个func参数
- def info():
- print('已有普通函数')
- info() # 执行__call__()方法
-
- 输出:
- 添加装饰功能
- 已有普通函数
学习导航:http://xqnav.top/