Day 18:yield 关键字、nonlocal 关键字和 global 关键字使用
yield关键字 + 与之相关的生成器函数
- yield和return的异同点:
相同点,函数执行遇到yield,和return一样,立即返回,可以这么说,yield 是一种特殊的 return。
不同之处在于,次进入函数时直接到 yield 的下一个语句,而 return 后再进入函数,还是从函数体的第一行代码开始执行。
- 带 yield 的函数是生成器,通常与 next 函数结合用。下次进入函数,便使用 next 函数进入到函数体内。
看例子:
def func2(): print('hello yeld! what\'s up...') yield 'mlnsssx!' print('hello again!') ret2 = func2()#执行func2并未打印任何东西,只得到一个生成器对象 next(ret2)#再执行 next(func2) 时,输出下面信息: hello yeld! what's up... 'mlnsssx!' #再次执行next(ret2) next(ret2) hello again! --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-11-51110e1e01e2> in <module>() 1 #再次执行next(ret2) ----> 2 next(ret2) StopIteration:
#再次执行next(ret2) --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-12-495dce9b11e2> in <module>() ----> 1 next(ret2) StopIteration:
第二次执行打印了yield之后的语句,然后迭代终止,抛出异常StopIteration ,可以捕获来判断迭代是否结束。
- yield用作生成器以节省内存
节省内存?是的,函数带有 yield,就是一个生成器,英文 generator,它的重要优点之一节省内存。
def myrange(stop): start = 0 while start < stop: yield start start += 1 for i in myrange(10): print(i) output: 0 1 2 3 4 5 6 7 8 9
空间复杂度都是 O(1),这得益于 yield 关键字,遇到就返回且再进入执行下一句的机制。
对比不使用yield生成器函数方法,实现同样的结果,可能是:
def myrange(stop): start = 0 result = [] while start < stop: result.append(start) start += 1 return result for i in myrange(10): print(i)
def myrange(stop): result = range(0,10) return result for i in myrange(10): print(i)
空间复杂度都是 O(n)。
- send 函数
send函数属于带 yield 的生成器对象,是它的封装方法:
def func3(): print('hello yeld! let\'s look at it...') while True: result = yield 4 if result: print('send me a value is:%d'%(result,)) return else: print('no send') g = func3() print(next(g)) print('ready to send') print(g.send(10)) output: hello yeld! let's look at it... 4 ready to send send me a value is:10 --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-30-661b5d675223> in <module>() 12 print(next(g)) 13 print('ready to send') ---> 14 print(g.send(10)) StopIteration:
分析:
g = func3()
:创建生成器对象,什么都不打印print(next(g))
:进入 f,打印hello yeld! let's look at it...,并 yield 后返回值 4,并打印 4print('ready to send')
print(g.send(10))
:send 值 10 赋给 result,执行到上一次 yield 语句的后一句打印出send me a value is:10
- 遇到 return 后返回,因为 f 是生成器,同时提示 StopIteration
send 函数的用法:它传递给 yield 左侧的 result 变量。其实好理解,谁yield出来了一个变量,下次进入执行时把新的值赋给他
return 后抛出迭代终止的异常,可以用作生成器函数终止的提示,捕获就好了。
yield使用例子
#使用yield实现嵌套list的完全展开 list嵌入list def deep_flatten(lst): for i in lst: if type(i)==list: yield from deep_flatten(i) else: yield i g = deep_flatten([1,['s',3],4,5,[1,2,3,4,'assd']]) for _ in g: print(_) output: 1 s 3 4 5 1 2 3 4 assd
nonlocal 关键字
关键词 nonlocal 常用于函数嵌套中,声明变量为非局部变量。
#函数嵌套 实现 不大于6时自增,否则置零后,再从零自增。 def func4(): i = 0 def auto_increase(): if i>=6: i=0 i+=1 ret = func4(): output: File "<ipython-input-40-3d18950ed11a>", line 7 ret = func4(): ^ SyntaxError: invalid syntax
显然抛出错误的原因是因为执行i>=6时 i 未定义。靠近变量 i 的函数是auto_increase()
,不是 func4,i 没有在 auto_increase中先赋值,所以报错。
要么传参,要么使用全局变量解决,当然最安全的是使用nonlocal 声明 i 不是 auto_increase 内的局部变量。
def func5(): i = 0 def auto_increase(): nonlocal i if i>=6: i=0 i+=1 ret = [] for _ in range(0, 12): auto_increase() ret.append(i) print(ret) func5() output: [1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6]
global 关键字
上面提到过全局变量,就是想在哪里使用某个变量的值都可以对吧,使用nonlocal又有一定的局限
以前理解的全局变量是这样的
i = 5 def f(): print(i) def g(): print(i) pass f() g() output: 5 5
老铁没毛病,但是
j = 5 def m(): j += 1 m() output: --------------------------------------------------------------------------- UnboundLocalError Traceback (most recent call last) <ipython-input-50-ade33d043d48> in <module>() 3 j += 1 4 ----> 5 m() <ipython-input-50-ade33d043d48> in m() 1 j = 5 2 def m(): ----> 3 j += 1 4 5 m() UnboundLocalError: local variable 'j' referenced before assignment
抛出异常 UnboundLocalError,原因是编译器在解释 j+=1
时,会解析 j 为函数 m() 内的局部变量, 但是编译器又找不到。
所以用上global
j = 5 def m(): global j j += 1 m() print(j) output: 6
在函数 m 内,显示地告诉解释器 j 为全局变量,然后,解释器会在函数外面寻找 j 的定义,执行完 j+=1
后,j 还为全局变量,值加 1
和c++中全局变量差不多。