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,并打印 4
  • print('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++中全局变量差不多。

posted @ 2021-07-06 16:03  PiaYie  阅读(52)  评论(0编辑  收藏  举报