-
闭包
定义
官方:内部函数对外部函数作用域里变量的引用
个人理解:对于一个内部函数,如果它引用了外部函数的参数和局部变量,且外部函数返回这个内部函数,那么我们把这个内部函数成为闭包函数。闭包函数和作用域外部的变量统称为闭包。
用法
举例,如下是一组嵌套函数fun和fun1
def fun(): #外部函数
print("This is fun")
def fun1(): #内部函数
print("This is fun1")
当我们运行fun()时,可以得到:
This is fun
当我们运行fun1()时,可以得到:
Traceback (most recent call last):
File "", line 1, in
NameError: name 'fun1' is not defined
说明内部函数不能直接被调用,func1 是建立在外部函数运行中才能调用。这里我们还需要去理解函数内部作用域的生命周期,只有执行期间才有效。如下:
>>>fun() #外部函数调用,内部函数创建
>>>fun1() #无法直接调用,创建在fun()中
这样连续执行命令,我们也无法调用fun1,因为fun()中的函数只有执行期间有效。
为此,我们要调用fun1,可以让fun外部函数返回fun1:
def fun():
print("This is fun")
def fun1():
print("This is fun1")
return fun1
test = fun() #fun1
这里test就为fun()返回的内部函数fun1;若要调用fun1这个函数,直接使用test()。
此外,可以让内部函数引用外部函数作用域中的变量,使这个变量保存下来,不会失效。如下变量num存在于test()中,相当于私有化了变量,完成了数据封装。
def fun():
num = 1
print("This is fun")
def fun1():
print(num)
return fun1
>>>test = fun()
>>>test()
This is fun
1
此外,py3之后,新增了nonlocal关键词,可以使内部函数修改外部函数的变量:
def createCounter():
n=0
def counter():
nonlocal n
n = 2
print(n)
counter()
print(n)
def createCounter2():
n=0
def counter():
n = 2
print(n)
counter()
print(n)
>>>createCounter()
2
2
>>>createCounter2()
2
0
注意:
返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
有这样一个例子:
def count():
fs = []
for i in range(1, 4):
def f():
return i*i
fs.append(f)
return fs
f1, f2, f3 = count()
>>> f1()
9
>>> f2()
9
>>> f3()
9
全部都是9
!原因就在于返回的函数引用了变量i
,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i
已经变成了3
,因此最终结果为9
。所以我们需要另外创建一个函数;
def count():
def f(j):
def g():
return j*j
return g
fs = []
for i in range(1, 4):
fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()
return fs
-
装饰器
装饰器函数的标志是:@
装饰器是基于闭包
装饰器的作用是,不影响原函数功能,添加新的功能。他是使函数执行之前先执行装饰器的内容。如下
def log(fun):
def f():
print("log装饰器之后调用Toprint函数")
# return fun #返回的是原函数这个对象
return fun() # 返回的是原函数的调用
return f
@log
def Toprint():
print("This is Toprint")
>>>Toprint()
log装饰器之后调用Toprint函数
This is Toprint
他的相当于log(Toprint)(),流程是log(Toprint)() --> return f --> f() --> print("log装饰器之后调用Toprint函数") --> return fun() --> print("This is Toprint")
即接收被装饰器的函数作为参数,并进行调用一次,(如果不调用,返回的只是f函数对象),即执行f(),打印,并执行原函数;
装饰器函数带参
我们不能直接在装饰器函数中传入参数和函数两个参数,需要另外创建一个函数,将参数和原函数分别传入,且在内部接收函数,外部接收参数。
def log(tcp_type):
def func(b_fun):
def fun():
if tcp_type == 'get':
print('getget')
if tcp_type == 'post':
print('postpost')
return b_fun()
return fun # 返回的是原函数的调用
return func
@log(tcp_type='get')
def get():
print("This is get")
@log(tcp_type='post')
def post():
print("This is post")
他的相当于log(tcp_type='get')()(),流程是先调用装饰器函数,返回func函数并调用,返回fun函数并调用,判断tcp_type,打印,调用原函数。
同时支持装饰器带参或者不带参
即:支持:
@log
def f():
pass
又支持:
@log('execute')
def f():
pass
我们可以在装饰器函数中写一个默认变量,来进行判断;
def log(tcp_type=None):
def func(b_fun):
def fun():
if tcp_type == 'get':
print('getget')
if tcp_type == 'post':
print('postpost')
return b_fun()
return fun # 返回的是原函数的调用
return func
@log()
def notcp():
print("notcp")
@log(tcp_type='get')
def get():
print("This is get")
@log(tcp_type='post')
def post():
print("This is post")
>>>notcp()
notcp
被装饰函数带参
被装饰函数的参数可以传入装饰函数的最内部函数中进行操作
def log(func):
def f(x,y):
x +=3
y +=4
return func(x,y)
return f
@log
def fun(a,b):
print(a+b)
>>>fun(3,4)
14
当我们内部函数可以接受任何参数调用时,可以这样写:def fun(*args, **kw)
def metric(fn):
def f(*args, **kw):
print('This is %s ' % (fn.__name__))
return fn(*args, **kw)
return f
@metric
def fast(x, y):
print(x + y)
@metric
def slow(x, y, z):
print(x * y * z)
f = fast(11, 22)
s = slow(11, 22, 33)