这里总结一波python中的闭包以及装饰器:
学习闭包以及装饰器首先要了解函数的作用域,在python中函数中变量的作用域优先级如下:
python中的变量作用域满足LEGB原则:
L:local函数内部作用域
E:enclosing函数内部与内嵌函数之间
G:global全局作用域
B:build_in内置作用域
优先级关系如下: L>E>G>B
注意:不是所有的语言都是支持函数内部可以访问函数外部的变量。比如说PHP中函数内部查找不到变量的时候是不会去函数外部查找的,除非使用global指定全局。在这方面,python和javascript比较类似。
了解了函数的作用域以后,接着了解一下什么是闭包:
闭包就是内部函数中对enclosing作用域中的变量进行引用。内部函数中如果含有外部函数定义的变量,则会将这些变量加入到内部函数的属性中来。
写个简单的python闭包程序,如下:
def get_value(passline):
print passline
print "%x"%id(passline)
def in_func():
print passline
print "%x"%id(passline)
return in_func
f=get_value(60)
get_value()函数中的内部函数in_func()中引用了外部定义的变量passline,所以就构成了一个简单的闭包。接下来需要简单分析一下,函数执行过程之中,变量以及内存的关系:
(1)f=get_value(60),get_value()函数执行完毕,理论上get_value()所占用的内存会被python解释器回收。但是因为get_value()函数返回的是in_func()函数,in_func()函数中调用了变量passline,所以get_value()函数执行完毕,资源没有完全释放掉。
(2):f是由get_value()返回的in_func()函数。可以查看f.__closure__属性,这个属性返回内部函数所占用的外部函数的,没有释放内存资源。
说了一大堆,闭包有什么用那?闭包可以进行代码的复用,减少代码的冗余。
举例说明一个:定义一个函数用来计算传递过来的参数之和,另外定义一个函数用来计算传递过来的参数的平均值,一般情况下,可以这样实现:
#求和函数
def my_sum(*args):
return sum(args)
#求平均值函数
def my_avg(*args):
return sum(*args)/len(args)
#调用
print(my_sum(1,2,3,4))
print(my_avg(1,2,3,4))
上述的实现方式没有问题,但是每个函数都不健壮,比如说缺少对数据类型的判断,缺少预防除零操作的手段等等,下面就进行一下修改:
#求和函数
def my_sum(*args):
if len(args) == 0:
return 0
for val in args:
if not isinstance(val,"int"):
return 0
return sum(args)
#求平均值函数
def my_avg(*args):
if len(args) == 0:
return 0
for val in args:
if not isinstance(val,"int"):
return 0
return sum(*args)/len(args)
#调用
print(my_sum(1,2,3,4))
print(my_avg(1,2,3,4))
上面对两个函数进行了优化,解决了上述提出的类型匹配,除零问题。但是可以发现,两个函数之中存在完全相同的部分。这也就是代码冗余了,为了提高代码的复用性以及健壮性,我们还需要解决冗余的问题。
如何解决上面的代码的冗余问题那?可以看一下,下面的代码:
#求和函数
def my_sum(*args):
return sum(args)
#求平均值函数
def my_avg(*args):
return sum(*args)/len(args)
#闭包
def dec(func):
def in_dec(*args):
if len(args) == 0:
return 0
for val in args:
if not isinstance(val,"int"):
return 0
func(*args)
return in_dec
my_sum_one=dec(my_sum)
my_avg_one=dec(my_avg)
#调用
print(my_sum_one(1,2,3,4))
print(my_avg_one(1,2,3,4))
上面的代码可以看到,我们将求和函数以及求平均值函数两者之间相同的代码放到了dec()函数中的内部函数in_dec()函数中了。这样也就解决了代码冗余的问题了。
上述代码虽然解决的问题,但是是怎么实现的那,这里分析一下:
(1):dec(my_sum)—执行dec()函数,传递的参数是my_sum()函数
(2):执行dec()函数,return in_dec()也就是说,dec()函数将返回in_dec()函数,所以dec(my_sum) 就等于 in_dec() —-其实是等于函数中的func变量等于my_sum的in_dec()
(3):又因为my_sum_one=dec(my_sum),所以my_sum_one 就等于函数中的func变量等于my_sum的in_dec()
(4):调用my_sum_one(1,2,3,4)就相当于调用in_dec(1,2,3,4),并且in_dec中的func变量等于my_sum
(5):执行in_dec(1,2,3,4),所以会进行参数类型的判断,参数数量的判断以及执行func()函数,也就是传入的my_sum()函数
(6):下面的执行求平均值,同样的道理。不是同一个函数,但是因为闭包,可以共用同一代码,这也就不会造成代码的冗余了
解释完了闭包,再来解释一下python中的装饰器。python中的装饰器其实就是闭包,只不过python在语法上是支持闭包的,python通过@闭包函数名称这种语法糖(装饰器)来实现闭包。下面还是举例说明一下:
#定义一个闭包函数
def abc_out(func):
def in_abc(*args):
if "fuck" in args:
print("")
return False
if "shit" in args:
print("")
return False
func(*args)
return in_abc
#通过装饰器这种语法糖的形式调用闭包函数
@abc_out
def print_value(value):
print(value)
#调用函数
print_value("fuck") ---打印为""
装饰器的调用:在调用print_value()函数的时候,python解释器会将函数print_value传递到闭包函数
abc_out()中执行,这也就实现了闭包。
在python中,装饰器不仅仅是普通函数的专利,类中的函数同样可以进行使用,使用方式与普通函数完全相同,这里就不在赘述了。