python闭包理解与使用
闭包:内部函数中对enclosing作用域的变量进行引用,看下面代码:
passline = 60
def checkFun(val):
print("%x" %id(val))
if val > passline:
print('pass')
else:
print('fail')
def in_fun():
print(val)
return in_fun
f = checkFun(77)
f() # in_fun方法
print(f.__closure__)
上面的代码中,当checkFun方法执行完成之后val的值是被释放的,那么我们调用checkFun的返回值,in_fun方法,会打印出来,其实python解释器将这个值放到方法的属性__closure__
中
那么这个和闭包有什么关系,试想如果某一门课程总分是150,那么我们可能需要设置90分为及格,改变上面的函数
def setPass(passline): # 调用setPass(60)时候passline = 60
def checkFun(val):
if val > passline: # 将passline 添加到checkFun的__closur__中
print('pass')
else:
print('fail')
return checkFun
f_100 = setPass(60)
f_100(45) #fail
f_150 = setPass(90)
f_150(109) #pass
上面的代码中,当调用setPass(60)时候,passline就等于60,那么这里返回的checkFun函数中,用到了passline,就会将passline添加到checkFun函数的__closure__
属性中,,所以这里即使setPass方法执行结束,内部的passline也已经释放,但是由于解释器将passline添加到了__closure__
属性中,所以我们依然可以获取其值,,这里返回的函数就成为闭包
使用闭包封装函数
在开始之前,我们先看下面栗子:
def my_sum(*arg):
return sum(arg)
def my_average(*arg):
return sum(arg) / len(arg)
print(my_sum(1,2,3)) # 6
print(my_average(1,2,3)) # 2
可以看到,上面的函数正常情况下可以正常输出,,那么如果我们传递一些非法参数呢?
def my_sum(*arg):
return sum(arg)
def my_average(*arg):
return sum(arg) / len(arg)
print(my_sum(1,2,3,'33'))
print(my_average())
此时程序运行,解释器会抛出下面异常:
修改上面的代码,添加如下判断:
def my_sum(*arg):
if len(arg) == 0:
return 0
for v in arg:
if not isinstance(v,int):
return 0
return sum(arg)
def my_average(*arg):
if len(arg) == 0:
return 0
for v in arg:
if not isinstance(v,int):
return 0
return sum(arg) / len(arg)
print(my_sum(1,2,3,'33')) # 0
print(my_sum(1,2,3)) # 6
print(my_average(1,2,3)) # 2
print(my_average()) # 0
可以看到上面的代码可以正常输出了,不会抛出异常,但是上面的两个方法,都使用到了同样的代码块,这就有很大的冗余性,这里我们使用闭包来封装上面相同的代码块。如下:
def my_sum(*arg):
return sum(arg)
def my_average(*arg):
return sum(arg) / len(arg)
def sameCode(fun):
def inFun(*arg):
if len(arg) == 0:
return 0
for v in arg:
if not isinstance(v,int):
return 0
return fun(*arg)
return inFun
my_sum = sameCode(my_sum)
print(my_sum(1,2,3))
my_average = sameCode(my_average)
print(my_average())
python装饰器
python装饰器:
- 装饰器用来装饰函数
- 返回一个函数对象
- 被装饰函数标识符指向返回的函数对象
- 语法:@dec
这里我们可以将上面的代码使用装饰器进行改造
def sameCode(fun):
print('inFun runs...') #当使用@sameCode装饰器时候,会执行其内部封装的inFun方法
def inFun(*arg):
print('inFun runs ++++')
if len(arg) == 0:
return 0
for v in arg:
if not isinstance(v,int):
return 0
return fun(*arg)
return inFun
@sameCode #这里需要注意的是,装饰器必须写为被装饰的函数名称
def my_sum(*arg):
print('my_sum runs...')
return sum(arg)
print(my_sum(1,2,3,4))
此时打印如下:
这里需要注意的是,装饰器必须写为被装饰的函数名称
使用上面的装饰器装饰完成之后,实际上my_sum函数此时就指向inFun函数
@python装饰器理解
我们再来看下面的栗子
def dec(func):
print('dec first...')
def in_dec():
print('in_dec runs...')
func()
print("dec end...")
return in_dec
@dec #使用@dec来装饰bar函数,此时会执行dec方法
def bar():
print('bar runs....')
bar() #装饰完成,bar函数指向返回的in_dec函数
此时运行结果如下:
那么如果我们的bar函数需要传递参数,我们可以这样做:
def dec(func):
print('dec first...')
def in_dec(num1, num2):
print('in_dec runs...')
func(num1,num2)
print("dec end...")
return in_dec
@dec
def bar(num1, num2):
print('bar runs....sum is :',num1+num2)
bar(2,3)
当我们使用@dec装饰器装饰玩bar函数之后,bar其实就指向了dec返回的in_dec函数,所以这里需要in_dec来接收参数,此时效果如下: