【Python】浅析闭包与装饰器
前言
在此之前,需要认清以下几点:
1,变量可以指向一个函数。
>>> f = abs #变量f指向abs
>>> f
<built-in function abs>
>>> f(-1)
1
2,函数名是指向函数的变量。
>>> abs = len #abs 指向函数len。
>>> abs
<built-in function len>
>>> abs(-2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: object of type 'int' has no len()
>>> abs('length')
6
3,函数也是对象;
>>> def funt():
... pass
...
>>> funt.__class__
<class 'function'>
>>> issubclass(funt.__class__,object)
True
4,一个函数可以作为另一个函数的参数
>>> def add(x,y,f):
... return f(x) + f(y)
...
>>> add(-9,2,abs)
11
闭包
闭包的定义有很多,但是不管怎么样定义,有几个点是不会变的!
1,函数嵌套,也就是函数内部再定义一个新函数,可以说是外函数和内函数。
2,内函数使用到外函数定义的局部变量
3,外函数返回内函数的引用,根据前言所说可以理解为内函数名,
举个栗子:
# outer是外部函数 x外函数的临时变量
def outer():
x = 1
# inner是内函数
def inner():
#在内函数中 用到了外函数的临时变量
print(x)
# 外函数的返回值是内函数的引用
return inner
#
f = outer()
f() # 1
1,返回引用。
对于闭包,在外函数outer中 最后return inner,我们在调用外函数 f = outer() 的时候,outer返回了inner,inner是一个函数的引用,根据前言所说,可以理解为变量,这个引用被存入了变量f中,相当于 f = inner()。所以接下来我们再进行f() 的时候,相当于运行了inner函数。一个函数,如果函数名后紧跟一对括号,相当于现在我就要调用这个函数,如果不跟括号,相当于只是一个函数的名字,里面存了函数所在位置的引用。
2,内函数使用到外函数定义的局部变量。
一般情况下,一个函数结束的时候,会把自己的临时变量都释放还给内存,之后变量都不存在了。但是闭包是一个特别的情况。外部函数发现,自己的临时变量会在将来的内部函数中用到,自己在结束的时候,返回内函数的同时,会把外函数的临时变量送给内函数绑定在一起。所以外函数已经结束了,调用内函数的时候仍然能够使用外函数的临时变量。
装饰器
闭包的一种应用就是装饰器,将一个函数作为外函数的参数传入,通过内函数对传入的函数进行修改装饰,再将内函数返回,装饰器不能影响原函数的功能,装饰器是独立出来的函数。谁调用它,谁就可以使用它的功能。
def decorated(func):
def logger(x,y):
print('x+y')
return func(x,y)
return logger
def add(x,y):
return x+y
add = decorated(add) # 对add进行装饰,改变其原有功能
print(add(-2,3))
简化代码,使用 @ 标识符将装饰器应用到函数
def decorated(func):
def logger(x,y):
print('x+y')
return func(x,y)
return logger
#只需要在函数的定义前加上@和装饰器的名称。
@decorated
def add(x,y):
return x+y
# add = decorated(add)
print(add(-2,3))
通用的装饰器
def decorated(func):
def logger(*arges,**kwarges):
print("arges={},kwarges={}".format(arges,kwarges))
return func(*arges,**kwarges)
return logger
@decorated
def add(x,y):
return x+y
print(add(x=9,y=3))
print(add(9,3))
print(add(9,y=3))
#输出结果:
arges=(),kwarges={'x': 9, 'y': 3}
12
arges=(9, 3),kwarges={}
12
arges=(9,),kwarges={'y': 3}
12
现在的add可以接收不同类型的参数。
收集参数
def print_params(*params):
print(params)
print_params('Testing') #('Testing',) 返回一个元祖
参数前面的一个星号将提供的所有值都放在一个元组中,也就是将这些值收集起来。但是一个星号不能收集关键字参数,
def print_params(*params):
print(params)
print_params(key='Testing')
#TypeError: print_params() got an unexpected keyword argument 'key'
要收集关键字参数,可使用两个星号,这样得到的是一个字典而不是元组。
def print_params(**params):
print(params)
print_params(key='Testing')
#{'key': 'Testing'} 返回一个字典
再举个栗子
def print_params(x, y, z=4, *pospar, **keypar):
print(x, y, z)
print(pospar)
print(keypar)
print_params(1, 2, 3, 5, 6, 7, foo=1, bar=2)
#输出结果
1 2 3
(5, 6, 7)
{'foo': 1, 'bar': 2}
综上觉得Python真的是一门很神奇的语言,有好多神奇和高效的特性,怪不得刚出现时,大家都说Python简单,但是要理解这些特性背后的东西,又需要琢磨一阵子。
参考
http://python.jobbole.com/81683/
《Python 基础教程》(第3版)