Skip to content

Latest commit

 

History

History
258 lines (191 loc) · 7.73 KB

19.协程-生成器.md

File metadata and controls

258 lines (191 loc) · 7.73 KB

生成器

这里不在解释什么是生成器以及生生成器的原理,主要是生成器的使用和方法

一.最简单的生成器

(i for i in range(3))

1.yield语句的用法总结

结构:

temp = yield 表达式(每次迭代要返回的值

如果要返回确定的值,后面的表达式不可省略,绝大部分情况下我们也不省略,否则只能返回None;

如果使用了send(value)。传递进去的那个value回取代那个表达式的值,并且会将传递进去的那个值返回给yield表达式的结果temp,所以如果想在yield后面使用传递进去的那个值,必须要有使用temp,否则无法使用;

二.send方法

def new_generator(n):
    for i in range(n):
        temp = yield i
        print(f"我是{temp}")


g = new_generator(5)
print(next(g))  # 输出0, 我是None
print(next(g))  # 输出1, 我是None
print(g.send(100))  # 输出2, 我是100, 将100 通过send传到temp
print(next(g))  # 输出3, 我是None
print(next(g))  # 输出4, 我是None

从上面可以看出yield语句与普通函数的return语句的区别在哪里了,主要集中在以下几点

1.return 不能写成“temp=return xxxx”的形式,会提示语法错误,但是yield可以写成“temp=yield xxxx”的形式;

2.普通函数return后面的语句都是不会再执行的,但是yield语句后面的依然会执行,但是需要注意的是,由于“延迟加载”特性,yield后面的代码并不是在第一次迭代的时候执行的,而是第二次迭代的时候才执行第一次yield后面没有执行的代码。也正是这个特性,构成了yield为什么是实现协程的最简单实现。

3.使用send()方法传进去的值,实际上就是yield表达式返回的值,这就是为什么前面每次输出print(temp)都打印出None,因为没有send值,所以temp为None,但是send(100)之后却打印100,因为此时temp就是100了。

1.send(arg)方法总结

它的主要作用是,当我需要手动更改生成器里面的某一个值并且使用它,则send发送进去一个数据,然后保存到yield语句的返回值,以提供使用。

send(arg)的返回值就是那个本来应该被迭代出来的那个值。 这样既可以保证我能够传入新的值,原来的值也不会弄丢。

三.throw方法

在生成器中抛出异常,并且这个throw函数会返回下一个要迭代的值或者是StopIteration。

def throw_generator():
    yield 'a'
    yield 'b'
    yield 'c'


g = throw_generator()
print(next(g))  # a
print(next(g))  # b
print('-------------------------')
print(g.throw(StopIteration))  # StopIteration
print(next(g))

因为在迭代完 b 之后,就触发了StopIteration异常,这相当于后面的 ‘c’ 已经没用了,跳过了c ,c再也不会执行,就中断了,所以后面的 'c'再也不会迭代,所以这里不会再返回任何值,返回的是StopIteration。

再看一个例子

def throw_generator():
    try:
        yield 'a'
        yield 'b'
        yield 'c'
        yield 'd'
        yield 'e'
    except ValueError:
        print('触发“ValueError"了')
    except TypeError:
        print('触发“TypeError"了')


g = throw_generator()
print(next(g))  # a
print(next(g))  # b
print('-------------------------')
print(g.throw(ValueError))  # 触发“ValueError"了”,奔溃报StopIteration错误
print('-------------------------')
print(next(g))
print(next(g))
print('-------------------------')
print(g.throw(TypeError))
print('-------------------------')
print(next(g))

当前面两次执行了a和b之后,向生成器扔进去一个异常,触发ValueError异常,这时候意味着try后面的c、d、e已经作废了,不会再有用,这个生成器已经终止了,因此g.throw()会返回StopIteration。

再看一个例子:

def my_generator():
    while True:
        try:
            yield 'a'
            yield 'b'
            yield 'c'
            yield 'd'
            yield 'e'
        except ValueError:
            print('触发“ValueError"了')
        except TypeError:
            print('触发“TypeError"了')


g = my_generator()
print(next(g))  # a
print(next(g))  # b
print('-------------------------')
print(g.throw(ValueError))  # 触发“ValueError"了 a
print('-------------------------')
print(next(g))  # b
print(next(g))  # c
print('-------------------------')
print(g.throw(TypeError))  # 触发“TypeError"了 a
print('-------------------------')
print(next(g))  # b

首先print(next(g))两次:会输出a、b,并停留在c之前。

然后由于执行了g.throw(ValueError),所以会跳过所有后续的try语句,也就是说yield 'c'、yield 'd'、yield 'e'不会被执行,

然后进入到except语句,打印出 触发“ValueError"了。

然后再次进入到while语句部分,消耗一个yield,此时因为是重新进入的while,小号的依然是第一个yield 'a',所以会输出a。实际上这里的a也就是g.throw()的返回值,因为它返回的是下一个迭代的数;

然后在print(next(g))两次,会执行yield b’、yield 'c’语句,打印出b、c,并停留在执行完该语句后的位置,即yield 'd'之前。

然后再g.throw(TypeError):会跳出try语句,从而后面的d,e不会被执行,下次自一次进入while,依然打印出a。最后,执行了一次print(next(g)),打印出b。

四.生成器启动关闭

1.启动

启动生成器,启动的两种方法为: 第一:直接使用next(g),这会直接开始迭代第一个元素(推荐使用这个启动) 第二:使用g.send(None)进行启动,注意第一次启动的时候只能传入None,如果传入其他具体的指则会报错哦!

def throw_generator():
    yield 1
    yield 2
    yield 3
    yield 4


g = throw_generator()
print(g.send(None))
print(next(g))
print(next(g))

2.关闭

使用close()方法

如果一个生成器被中途关闭之后,在此调用next()方法,则会显示错误

def my_generator():
    yield 1
    yield 2
    yield 3
    yield 4


g = my_generator()
print(next(g))
print(next(g))
g.close()
print(next(g))  # 在此处会显示错误

五.终止迭代

关闭生成器,使用close()方法,后面的迭代或抛出StopIteration异常。另外

在一个生成器中,如果没有return,则默认执行到函数完毕时返回StopIteration

def g1():
    yield 1
g=g1()
next(g)
next(g)

如果在执行过程中 return,则直接抛出 StopIteration 终止迭代。

def g2():
    yield 'a'
    return
    yield 'b'
    
g=g2()
next(g)
next(g)

如果在return后返回一个值,那么这个值为StopIteration异常的说明,不是程序的返回值。

def g3():
    yield 'a'
    return '这是错误说明'
    yield 'b'   
    
g=g3()
next(g)
next(g)

注意:

生成器没有办法使用return来返回值。因为return返回的那个值是通过StopIteration的异常信息返回的,所以我没办法直接获取这个return返回的值。

当然上面所说的无法获取return返回值,我们指的是没有办法通过result=g3()这种形式获取return的返回值。

实际上还是可以获取这个return的值的,有两种方法: 方法一:使用后面的yield from 语句 方法二: 因为return返回的值是作为StopIteration的一个value属性存在的,StopIteration本质上是一个类,所以可以通过访问它的value属性获取这个return返回的值。使用下面的代码:

def g3():
    yield 'a'
    return '这是错误说明'
    yield 'b'
g=g3()
 
try:   
    print(next(g))  #a
    print(next(g))  #触发异常
except StopIteration as exc:
    result=exc.value
    print(result)