一、闭包
闭包定义:在函数嵌套(函数内再定义函数)的前提下,内部函数使用了外部函数变量或参数,且外部函数返回了内部函数,则将这个使用了外部函数变量的内部函数称为闭包
闭包作用特点
python">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
1.1闭包的使用
根据配置使用闭包实现多人之间的对话
python">def configName(name):
def sayInfo(info):
print(name + ": " + info)
return sayInfo
xy = configName("XY")
xy("你好,在不")
xy("你好!在不在?")
yx = configName("YX")
yx("不在。")
输出:
XY: 你好,在不
XY: 你好!在不在?
YX: 不在。!
1.2修改闭包内使用的外部变量
使用nonlocal关键字完成,具体举例如下
python">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
二、装饰器
装饰器定义:指为已有函数增加额外功能的函数,本质上就是一个闭包函数,闭包函数有且只有一个参数并为函数类型,规定已实现的功能代码不允许被修改,但可以被扩展
装饰器功能特点
- 不修改已有函数的源代码
- 不修改已有函数的调用方式
- 为已有函数增加额外的调用功能
python">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函数,调用方式不变
输出:
额外功能……
已有普通函数……
2.1装饰器使用
实例:装饰器实现统计已有函数执行时间,可知装饰器能够在不改变已有函数源代码及调用方式的前提下,对已有函数进行功能的扩展
python">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
2.2通用装饰器的使用
1.装饰器带有参数的函数
python">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.装饰器带返回值的函数
python"># 装饰器带返回值的函数
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.装饰带不定长参数的函数
python"># 装饰带不定长参数和返回值的函数
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.通用装饰器
python"># 通用装饰器
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
2.3多个装饰器的使用
装饰过程:离函数最近的装饰器先装饰,然后外面的装饰器再进行装饰,即由内到外的装饰
python">def outFunc(func):
def inFunc(*args, **kwargs): # 使用装饰器装饰已有函数时,内部函数类型和已有函数保持一致
return "<div>" + func() + "</div>"
return inFunc
def outFunc2(func):
def inFunc2(*args, **kwargs): # 使用装饰器装饰已有函数时,内部函数类型和已有函数保持一致
return "<p>" + func() + "</p>"
return inFunc2
@outFunc
@outFunc2
def info():
return "Hello World!"
result = info() # 首先执行inFunc
print('结果为:', result)
输出:
结果为: <div><p>Hello World!</p></div>
单步调试执行过程如下
2.4带参数的装饰器
使用带参数的装饰器,需在装饰器外再包裹一个函数,该函数用来接收参数,返回是装饰器,因为 @符号需要配合装饰器实例使用
python">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
2.5类装饰器
- 要想类的实例对象能够像函数一样调用,需在类中使用call方法,把类的实例变成可调用对象(callable),即能像调用函数一样进行调用
- 在call方法里进行对func函数的装饰,可以添加额外的功能
python">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/