CONTENTS
- 局部变量与全局变量
- 嵌套函数
- 高阶函数
- 装饰器
- 迭代器与生成器
局部变量与全局变量
局部变量与全局变量的区别在于二者的作用域不同。
- 局部变量只在函数中生效,函数就是局部变量的作用域
- 全局变量也可以在函数中被访问,但不能再函数中修改,除非声明。
1 num = 1 # 定义一个全局变量 2 def func1(): 3 print(num) 4 return print("func1执行完毕") 5 func1() # 可以访问到全局变量
在函数中是可以访问全局变量的值。如果想在函数中修改全局变量,需要利用global声明:
1 def func2(): 2 global num 3 num = num + 1 4 print(num) 5 return print("func2执行完毕") 6 func2() # 虽然可以访问,但未声明的话是不能修改的 7 print(num)
在函数中对全局变量进行修改,num在全局范围中的值也相应发生了变化。
还有一个和局部变量相关的关键字nonlocal,字面意思就是指当前的这个变量不是局部变量。nonlocal是Python3中新增的关键字,python2.x不支持。
nonlocal一般用于嵌套函数中:
1 def func4(): 2 num = 1 3 def func5(): 4 nonlocal num 5 num = num+1 6 print(num) 7 return print("func5执行完毕") 8 func5() 9 return print("func4执行完毕") 10 func4()
func5中num修改的值就是func4中定义的num。
嵌套函数
嵌套函数,顾名思义就是函数内又定义了一个或多个函数。
1 def func4(): 2 num = 1 3 def func5(): 4 nonlocal num 5 num = num+1 6 print(num) 7 return print("func5执行完毕") 8 # func5() 9 return print("func4执行完毕") 10 func4()
执行上述代码,func5并没有被执行,并且在函数声明外部也不能被调用。因为函数即“变量”,嵌套函数相当于是局部变量,不能在函数外部调用。如果要执行func5,需要取消第8行的注释,因为函数只有调用了才会执行。
高阶函数
在讨论高阶函数之前,还是再来讨论一下为什么函数即“变量”。
在使用函数进行调用时,比如说abs(),我们会将一个参数值输入到abs中,比如-1,再用一个变量获取返回值,完成一个函数的调用。
1 a = abs(-1)
那么,abs(-1)是函数调用,而abs是函数本身。
如果将函数本身赋值给变量又会发生什么呢?
1 a = abs 2 print(a(-1))
执行上述代码,会获得与abs(-1)一样的结果。那么,此时abs是不是相当于变量一般的把自己赋值给了a,使得a有了和自己一样的功能呢?也就是说,此时a已指向abs,那么函数名其实也就是指向函数的一个变量。对于函数abs(),完全可以把函数名abs看成是变量,它指向了一个可以计算绝对值的函数。那问题又来了,既然abs可以看成是变量,是不是可以给它赋值呢?
1 abs=10 2 print(abs)
好吧,这样居然也是可以的。也就是或这个时候,abs已经是指向10的变量,而不是那个能计算绝对值的函数了。那再调用一下abs函数试试?
abs=10 print(abs) print(abs(-1))
这时,会返回一个“TypeError: 'int' object is not callable”。也就是说,abs现在是整型10了。
好啦,扯了一大堆,似乎只说明了一个问题:函数即“变量”。那么,函数在传入参数时,是否可以将函数传入呢?这种函数就是高阶函数。
高阶函数:一个函数可以接受另一个函数作为参数。一个高阶函数的例子:
1 def gate_cal(wh, wx, ht_pre, x, b, activation): # activation 直接传入函数 --> 高阶函数 2 '''由于各门的计算公式形式相同,只是参数 激活函数不一样,为此生成一个公式生成函数''' 3 x = x.reshape(-1, 1) 4 return activation(wh.dot(ht_pre) + wx.dot(x) + b)
在计算LSTM遗忘门、输入门、输出门、状态时由于运算公式都一样,所以可以写成上述代码中的函数。其中,activation就是传入的一个函数(激活函数)。
函数作为变量传入时,只需要传入函数名即可,也就是如果传入的激活函数是sigmoid函数:
- 只需要将sigmoid传入参数即可。 √
- 而不是将sigmoid()传入, 这种是把调用后的返回值传入。 ×
装饰器
装饰器本质上是函数,从字面上理解就是起到装饰的作用,装饰其他函数。也就是为其他函数添加附加功能。
原则:不能修改被装饰函数的源代码和调用方式。
在某种意义上,装饰器对于被装饰的函数是完全透明的。
首先,装饰器的实现是基于对之前内容的运用,即:
- 函数即“变量”
- 嵌套函数
- 高阶函数
例如,对于一个函数func1,想要利用装饰器对其添加运行时间的功能。
1 def func1(): 2 time.sleep(3) 3 print("in the func")
首先,如果只是将func1传给另一个函数(应用高阶函数),在另一个函数中添加功能,这样会改变func1的调用方式:
1 def test(func): 2 start_time = time.time() 3 func() 4 print("运行时间:", time.time() - start_time) 5 6 test(func1)
此时,确实实现了添加运算时间的功能,但实现方式变成了test(func1),改变了func1()这样的调用方式。
其次,想着套上嵌套函数的功能看看能不能实现。为了让内部嵌套的韩式能够自己调用,将deco的返回值返回test:
1 import time 2 3 def func1(): 4 time.sleep(3) 5 print("in the func") 6 7 def deco(func): 8 def test(): 9 start_time = time.time() 10 func() 11 print("运行时间:", time.time()-start_time) 12 return test
但这个时候好像调用还是需要deco(func1),和原来的调用还不一样。这时,是不是会想到之前介绍的abs,函数即“变量”,即:
13 func1 = deco(func1) 14 func1()
这样一个装饰器就完成了!!!
其实在python中有专门的语句,可以省去func1 = deco(func1)这样的操作,即:使用@deco这样一条语句对func1(要装饰的函数)进行装饰
@deco 实际上就是完成了func1 = deco(func1)这样的操作
1 import time 2 3 def deco(func): 4 def test(): 5 start_time = time.time() 6 func() 7 print("运行时间:", time.time()-start_time) 8 return test 9 10 @deco # func1 = deco(func1) 11 def func1(): 12 time.sleep(3) 13 print("in the func") 14 15 func1()
这才是python上实现一个简易装饰器的模样。
需求又来了, 如果func1函数想要传参数进去,需要在嵌套函数的内部函数中添加位置参数或关键字参数,然后再在func调用时传入,如代码注释部分所示:
1 import time 2 3 def deco(func): 4 def test(*arg1): #1. 参数添加 5 start_time = time.time() 6 func(*arg1) # 2. 传进的参数传入 7 print("运行时间:", time.time()-start_time) 8 return test 9 10 @deco 11 def func1(*args): 12 time.sleep(3) 13 print("in the func") 14 print(args) 15 16 func1(1,1,1,1,1)
需求再来一波~... 让同一个装饰器对不同的函数装饰,产生不同的装饰效果。
可以在@deco后面进行一些操作,即@deco() 里传入参数,进行一些设定和判断。
如果@deco也可以传入参数,函数体中还需要在最外层增加一层嵌套函数来接收@deco()传入的参数,在函数中,通过传入的参数不同,可以声明不同的功能。
下述代码中,在最外层增加了一层auth()来接收auth_type,在最内层的函数中,可以根据auth_tpye的不同,定义不同的功能。这样在装饰不同函数时,将添加对应auth_type的内的功能。
1 user, passwd = "Iris", "123456" 2 3 def auth(auth_type): 4 print("auth_type:", auth_type) 5 def outer_wrapper(func): 6 def wrapper(*args, **kwargs): 7 if auth_type == "local": # 1.一种auth_type的功能 8 username = input("username:").strip() 9 password = input("password:").strip() 10 if user == username and passwd == password: 11 print("