Python 高级编程和异步IO并发编程 --12_6 生成器进阶- send、close和throw方法

生成器的一大特点:可以实现暂停,从生成器到协程,还有一些问题需要解决。

def gen_func():
    yield 1
    yield 2
    yield 3
    return "Tom"

if __name__=="__main__":
    gen = gen_func()  # 生成生成器对象
    print(next(gen))  #1 # 生成器实现了迭代协议,可用直接是由next
    print(next(gen))  #2 
    print(next(gen))  #3
    print(next(gen))  # 抛出异常,"StopIteration: Tom",停止迭代,并返回值Tom

生成器的特性:

1. 生成器不止可以产生值,还可以接收值

def gen_func():
    html1=yield "http://www.baidu.com"  # 该段代码的含义:1.可用产出值;2.可用接收值(调用方传递回来的值)
    print(html1)  # 调用方返回的值可以传递到生成器内部,获取值后打印值。打印输出Tom
    yield 2
    yield 3
    return "Tom-1"

if __name__=="__main__":
    gen = gen_func()
    url = next(gen)
    # download url
    html1 = "Tom"
    gen.send(html1)  # send方法可以传递值进入生成器内部,同时还可以重启生成器执行到下一个yield位置。
# 任何时候,任何地方只要拿到生成器对象就可以对它进行操作,包括暂停/恢复,要将值传递进去,就不可用next方法,next适用于直接yield值的场合。
#1. 启动生成器的方法有两种,next() 与 send(),send可用将值发送到内部,同时启动生成器。

既然send方法与next方法有相同的效果,则:

def gen_func():
    html1=yield "http://www.baidu.com"  # 该段代码的含义:1.可用产出值;2.可用接收值(调用方传递回来的值)
    print(html1)  # 调用方返回的值可以传递到生成器内部,获取值后打印值。打印输出Tom
    yield 2
    yield 3
    return "Tom-1"

if __name__=="__main__":
    gen = gen_func()
    url = next(gen)
    # download url
    html1 = "Tom"
    print(gen.send(html1)) # 2 执行的结果与print(next(gen))的结果一样,yield 2

调用send方法

def gen_func():
    html1=yield "http://www.baidu.com"  # 该段代码的含义:1.可用产出值;2.可用接收值(调用方传递回来的值)
    print(html1)  # 调用方返回的值可以传递到生成器内部,获取值后打印值。打印输出Tom
    yield 2
    return "Tom-1"

if __name__=="__main__":
    gen = gen_func()
    # 在调用send发送非None值之前,我们必须启动一次生成器,方式有两种:1.直接调用gen.send(None),2.next(gen)
    # url = gen.send(gen)  # TypeError: can't send non-None value to a just-started generator
    url = gen.send(None)   # 生成器第一次执行时,不能send一个非None的值,只能send None,为什么?
    # send方法将值发送给”html1“这个对象,生成器初始化时只是一个生成器对象,如果刚开始时根本没有执行过生成器,就无从运行到
    # “html1=yield "http://www.baidu.com"”这一行代码,所以send一个非None的值进来时,系统就会报错。
    
    # download url
    html1 = "Tom"
    print(gen.send(html1)) # 2 执行的结果与print(next(gen))的结果一样,yield 2
#   print(gen.send(html1)) # 由于上面只yield了一次,如果再次调用该语句就会抛出异常

close

def gen_func():
    yield "http://www.baidu.com"
    yield 2
    yield 3
    return "Tom-1"

if __name__=="__main__":
    gen = gen_func()
    print(next(gen))   # http://www.baidu.com,它会在执行yield "http://www.baidu.com"语句
                       # 后抛出异常, 抛出StopIteration异常
    gen.close()  
    next(gen)    
def gen_func():
    try:
        yield "http://www.baidu.com"
    except GeneratorExit:
        pass  # 调用gen.close()后,不管有没有catch到异常,生成器已经结束,他就会抛出异常。
    yield 2
    yield 3
    return "Tom-1"

if __name__=="__main__":
    gen = gen_func()
    print(next(gen))   
    gen.close()  # 抛出 RuntimeError: generator ignored GeneratorExit,已经将GeneratorExit忽略掉了
    next(gen)   
def gen_func():
    try:
        yield "http://www.baidu.com"
    except GeneratorExit:
        raise StopIteration
    yield 2
    yield 3
    return "Tom-1"

if __name__=="__main__":
    gen = gen_func()
    print(next(gen))
    gen.close()  # 此时,close就不会抛出异常
    next(gen)  # 由于注释掉yield 2/yield 3,在调用next(gen)方法时抛出异常,StopIteration,已经将GeneratorExit忽略掉了
def gen_func():
    yield "http://www.baidu.com"
    yield 2
    yield 3
    return "Tom-1"

if __name__=="__main__":
    gen = gen_func()
    print(next(gen))
    gen.close()  # 此时,close不会抛出异常
    next(gen)  # 只有在再次调用是才抛出异常,StopIteration
def gen_func():
    try:
        yield "http://www.baidu.com"
    except GeneratorExit:
        pass
    yield 2
    yield 3
    return "Tom-1"

if __name__=="__main__":
    gen = gen_func()
    print(next(gen))
    gen.close()  # 由于前面有了try/catch,执行到gen.close()就会抛出异常,RuntimeError: generator ignored GeneratorExit
    print("Hello")  # 该代码不会被执行
    # 异常会反映到调用方gen.close()里面,即调用close的地方抛出异常。gen.close()后面的代码不会被执行。
def gen_func():
    try:
        yield "http://www.baidu.com"
    except Exception: # 此处不去处理GeneratorExit异常,后面就不会抛出异常,代码均可执行
        pass
    yield 2
    yield 3
    return "Tom-1"

if __name__=="__main__":
    gen = gen_func()
    print(next(gen))  # http://www.baidu.com
    gen.close()
    print("Hello")  # hello

为什么?GeneratorExit 是继承至BaseException,不是Exception

def gen_func():
    try:
        yield "http://www.baidu.com"
    except BaseException: 
        pass
    yield 2
    yield 3
    return "Tom-1"

if __name__=="__main__":
    gen = gen_func()
    print(next(gen))  # http://www.baidu.com
    gen.close()       # 此时继续抛出RuntimeError: generator ignored GeneratorExit
    print("Hello")  # 该语句不被执行

Throw方法

def gen_func():
    yield "http://www.baidu.com"
    yield 2
    yield 3
    return "Tom-1"

if __name__=="__main__":
    gen = gen_func()
    print(next(gen))  # http://www.baidu.com
    gen.throw(Exception,"Download Error!")  # Exception: Download Error!
def gen_func():
    try:
        yield "http://www.baidu.com"
    except Exception as e:
        pass
    yield 2
    yield 3
    return "Tom-1"

if __name__=="__main__":
    gen = gen_func()
    print(next(gen))  # http://www.baidu.com
    gen.throw(Exception,"Download Error!")  # 可以顺利执行,并没有报错
    print(next(gen)) # 3
def gen_func():
    try:
        yield "http://www.baidu.com"
    except Exception as e:
        pass
    yield 2
    yield 3
    return "Tom-1"

if __name__=="__main__":
    gen = gen_func()
    print(next(gen))  # http://www.baidu.com
    gen.throw(Exception,"Download Error!")  # 可以顺利执行,并没有报错
    print(next(gen)) # 3
    gen.throw(Exception,"Download Error!")  # Exception: Download Error!
发布了380 篇原创文章 · 获赞 129 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/f2157120/article/details/105183650