大师兄的Python学习笔记(十四): 迭代器、生成器和协程

2020-03-24  本文已影响0人  superkmi

大师兄的Python学习笔记(十三): 理解装饰器
大师兄的Python学习笔记(十五): Socket编程

一、关于迭代器(Iterator)

1. 可迭代对象
1.1 集合数据类型
# dict
>>>d = dict(a=1,b=2)
>>>for i in d:
>>>    print(i)
a
b

# list
>>>l = list([1,2,3,4,5])
>>>for i in l:
>>>    print(i)
1
2
3
4
5

# string
>>>s = 'Hello World!'
>>>for i in s:
>>>    print(i)
H
e
l
l
o
 
W
o
r
l
d
!

# tuple
>>>t = (1,2,3,4,5)
>>>for i in t:
>>>    print(i)
1
2
3
4
5

# set
>>>s = {1,2,3,4,5}
>>>for i in s:
>>>    print(i)
1
2
3
4
5
1.2 生成器(generator)
>>>def gen_workingday():
>>>    days = ['mon','tue','wed','thu','fri']
>>>    for d in days:
>>>        yield d # 每次迭代的代码会储存在这里

>>>for d in gen_workingday(): 
>>>    print("today is {}".format(d))
today is mon
today is tue
today is wed
today is thu
today is fri
1.3 生成器表达式
>>>days = ['mon','tue','wed','thu','fri']
>>>wd = (d for d in days)

>>>while True:
>>>    try:
>>>        print("today is {}".format(next(wd)))
>>>    except StopIteration:
>>>        print("End of generator!")
>>>        break
today is mon
today is tue
today is wed
today is thu
today is fri
End of generator!
2. 迭代器
>>>def gen_workingday():
>>>    days = ['mon','tue','wed','thu','fri']
>>>    for d in days:
>>>        yield d
>>>        print("today is {}".format(d)) # 放在这里还是会被执行

>>>wd = gen_workingday()
>>>while True:
>>>    try:
>>>        next(wd)
>>>    except StopIteration:
>>>        print("End of generator!")
>>>        break
today is mon
today is tue
today is wed
today is thu
today is fri
End of generator!

二、关于协程(Coroutine)

1. 协程代码的实现
>>>def cor(): 
>>>    print("start")
>>>    x = yield
>>>    print("预激\n")

>>>    y = yield 1
>>>    print("第一次yield\n")
  
>>>    z = yield 2
>>>    print("第二次yield\n")
 
>>>if __name__ =="__main__": # 主进程
>>>    c = cor() 
>>>    c.send(None) # 预激协程
>>>    try:
>>>        a = c.send(1) # 调用协程
>>>        print("received_a:",a)
>>>        b = c.send(2)
>>>        print("received_b:",b)
>>>        c = c.send(3) # 这里会触发exception,所以后面的print不会被执行
>>>        print("received_c:",c)
>>>    except StopIteration as e:
>>>        print("StopIteration触发",e)
start
预激

received_a: 1
第一次yield

received_b: 2
第二次yield

StopIteration触发 
>>>def producer(cons):
>>>    cons.send(None) # 第3步, 预激活
>
>>>    for n in range(1,5): # 第7步
>>>        print("正在生产第{}件产品。".format(n)) # 第8步
>>>        c = cons.send(n) # 第9步, 触发生成器并返回值
>>>        print(c) # 第13步
>>>    cons.close()
>
>>>def consumer():
>>>    r = "" # 第4步
>>>    while True: # 第5步
>>>        n = yield r # 第6步, 返回并切换到producer / # 第12步,将r作为值返回
>>>        if not n: # 第10步
>>>            return
>>>        r = "正在消费第{}件产品。".format(n) # 第11步
>
>>>if __name__ == "__main__":
>>>    c = consumer() # 第1步, 构造生成器
>>>    producer(c) # 第2步, 调用函数
正在生产第1件产品。
正在消费第1件产品。
正在生产第2件产品。
正在消费第2件产品。
正在生产第3件产品。
正在消费第3件产品。
正在生产第4件产品。
正在消费第4件产品。
2. 协程的四种状态
状态 含义
GEN_CREATED 等待开始执行
GEN_RUNNING 解释器正在执行
GEN_SUSPENDED 在yield表达式处暂停
GEN_CLOSED 执行结束
>>>from inspect import getgeneratorstate

>>>def cor(): # 简单的协程
>>>    print("start")
>>>    print("**状态:{}**".format(getgeneratorstate(c))) # GEN_RUNNING
>>>    x = yield
>>>    print("预激\n")
    
>>>    y = yield 1
>>>    print("第一次yield\n")

>>>    z = yield 2
>>>    print("第二次yield\n")

>>>if __name__ =="__main__": # 主进程
>>>    c = cor() 
>>>    print("**状态:{}**".format(getgeneratorstate(c))) # GEN_CREATED
>>>    c.send(None) # 预激协程
>>>    try:
>>>        print("**状态:{}**".format(getgeneratorstate(c))) # GEN_SUSPENDED
>>>        a = c.send(1) # 调用协程
>>>        print("received_a:",a)
>>>        b = c.send(2)
>>>        print("received_b:",b)
>>>        c = c.send(3) # 这里会触发exception,所以后面的print不会被执行
>>>        print("received_c:",c)
>>>    except StopIteration as e:
>>>        print("StopIteration触发",e)
>>>        print("**状态:{}**".format(getgeneratorstate(c))) # GEN_CLOSED
**状态:GEN_CREATED**
start
**状态:GEN_RUNNING**
**状态:GEN_SUSPENDED**
预激

received_a: 1
第一次yield

received_b: 2
第二次yield

StopIteration触发 
**状态:GEN_CLOSED**
3. 终止协程
3.1 方法一:通过抛出异常终止

1)直接抛出异常

  • 通过raise StopIterationgenerator.throw(<exception>)方式直接抛出异常终止协程。
>>>def cor(): # 简单的协程
>>>    print("start")

>>>    x = yield
>>>    print("预激\n")
       
>>>    y = yield 1
>>>    print("第一次yield\n")
           
>>>    z = yield 2
>>>    print("第二次yield\n")
   
>>>if __name__ =="__main__": # 主进程
>>>    c = cor() 

>>>    c.send(None) # 预激协程
>>>    try:

>>>        a = c.send(1) # 调用协程
>>>        print("received_a:",a)
       
>>>        c.throw(StopIteration) # 直接抛出异常
       
>>>        b = c.send(2)
>>>        print("received_b:",b)
>>>        c = c.send(3) 
>>>        print("received_c:",c)
>>>    except RuntimeError as e:
>>>        print("exception触发",e)
start
预激

received_a: 1
exception触发 generator raised StopIteration

2)generator.close()

  • generator.close()方法触发StopIteration异常。
>>>def cor(): # 简单的协程
>>>    print("start")

>>>    x = yield
>>>    print("预激\n")
       
>>>    y = yield 1
>>>    print("第一次yield\n")
           
>>>    z = yield 2
>>>    print("第二次yield\n")
   
>>>if __name__ =="__main__": # 主进程
>>>    c = cor() 

>>>    c.send(None) # 预激协程
>>>    try:

>>>        a = c.send(1) # 调用协程
>>>        print("received_a:",a)
       
>>>        c.close() # 终止协程
       
>>>        b = c.send(2)
>>>        print("received_b:",b)
>>>        c = c.send(3) # 这里会触发exception,所以后面的print不会被执行
>>>        print("received_c:",c)
>>>    except StopIteration as e:
>>>        print("StopIteration触发",e)
start
预激

received_a: 1
StopIteration触发 
3.2 方法二:通过哨符值
>>>def cor(): # 子生成器
>>>    print("start")
    
>>>    x = yield
>>>    print("预激\n")
        
>>>    y = yield 1
>>>    if y is Ellipsis: # 捕获哨符值,并终止协程,触发StopIteration
>>>        return
    
>>>    print("第一次yield\n")
            
>>>    z = yield 2
>>>    print("第二次yield\n")
    
>>>if __name__ =="__main__": # 主进程
>>>    c = cor() 

>>>    c.send(None) # 预激协程
>>>    try:
>>> 
>>>        a = c.send(1) # 调用协程
>>>        print("received_a:",a)
        
>>>        c.send(Ellipsis) # 发送哨符值
       
>>>        b = c.send(2)
>>>        print("received_b:",b)
>>>        c = c.send(3) # 这里会触发exception,所以后面的print不会被执行
>>>        print("received_c:",c)
>>>    except StopIteration as e:
>>>        print("StopIteration触发",e)
start
预激

received_a: 1
StopIteration触发
4. yield from

1)创建与生成器之间的双向管道

  • 这里的逻辑是创建了一个生成器和主线程之间的管道,每次使用yield from,会调用一次连接的生成器。
>>>d = {'a':1,'b':2,'c':3,'d':4,'e':5}

>>>def gen():
>>>    yield from d

>>>g = gen()

>>>while True:
>>>    try:
>>>        print(d[g.send(None)])
>>>    except StopIteration:
>>>        print('end of gen.')
>>>        break
1
2
3
4
5
end of gen.

2)协程的委派生成器

  • 如果理解了上一个案例,就可以理解协程之间的双向管道。
  • 委托生成器只是将主线程yield的内容在主线程和协程中传递。
  • 委托生成器可以让代码更灵活间接,方便处理异常。
>>># 子协程 
>>>def average_gen(): 
>>>    print('cor started...')
>>>    while True:
>>>        x = yield
>>>        if x is None: # 哨兵值
>>>            break
>>>        print('recieved:',x)
>>>    return x
   
>>># 委托生成器
>>>def proxy_gen():
>>>    while True:
>>>        x = yield from average_gen() # x 只有在yield完全结束才会被赋值
>>>        if x is None:
>>>            break
   
>>>if __name__ == "__main__":
>>>    gen = proxy_gen()
>>>    gen.send(None)
>>>    gen.send(1)
>>>    gen.send(2)
>>>    gen.send(3)
>>>    try:
>>>        gen.send(None)
>>>    except StopIteration:
>>>        print("end of proxy gen.")
cor started...
recieved: 1
recieved: 2
recieved: 3
end of proxy gen.

三、asyncio包

1. @asyncio.coroutine
2. asyncio.get_event_loop()
3. asyncio.sleep(<t>)
4. EventLoop.run_until_complete(<coroutine>)
>>>import asyncio

>>>@asyncio.coroutine
>>>def cor():
>>>    print("start cor...")
>>>    r = yield from asyncio.sleep(5)
>>>    print("hello world!")

>>>loop = asyncio.get_event_loop()
>>>loop.run_until_complete(cor())
>>>loop.close()
start cor...
hello world!
5. asyncio.wait(<tasks>)
>>>import asyncio

>>>@asyncio.coroutine
>>>def cor_a():
>>>    print("start cor_a...")
>>>    r = yield from asyncio.sleep(2)
>>>    print("hello world from cor_a!")

>>>@asyncio.coroutine
>>>def cor_b():
>>>    print("start cor_b...")
>>>    r = yield from asyncio.sleep(5)
>>>    print("hello world from cor_b!")

>>>task = [cor_a(),cor_b()]
>>>loop = asyncio.get_event_loop()
>>>loop.run_until_complete(asyncio.wait(task))
>>>loop.close()
start cor_a...
start cor_b...
hello world from cor_a!
hello world from cor_b!
6. asyncio.gather(<task1>,<task2>...)
>>>import asyncio

>>>@asyncio.coroutine
>>>def cor_a():
>>>    print("start cor_a...")
>>>    r = yield from asyncio.sleep(2)
>>>    print("hello world from cor_a!")

>>>@asyncio.coroutine
>>>def cor_b():
>>>    print("start cor_b...")
>>>    r = yield from asyncio.sleep(5)
>>>    print("hello world from cor_b!")

>>>loop = asyncio.get_event_loop()
>>>loop.run_until_complete(asyncio.gather(cor_a(),cor_b()))
>>>loop.close()
start cor_a...
start cor_b...
hello world from cor_a!
hello world from cor_b!
7. asyncio.ensure_future(<cor>)
>>>import asyncio

>>>@asyncio.coroutine
>>>def cor_a():
>>>    print("start cor_a...")
>>>    r = yield from asyncio.sleep(2)
>>>    print("hello world from cor_a!")

>>>@asyncio.coroutine
>>>def cor_b():
>>>    print("start cor_b...")
>>>    r = yield from asyncio.sleep(5)
>>>    print("hello world from cor_b!")

>>>asyncio.ensure_future(cor_a())
>>>b = asyncio.ensure_future(cor_b())
>>>loop = asyncio.get_event_loop()
>>>loop.run_until_complete(b)
>>>loop.close()
start cor_a...
start cor_b...
hello world from cor_a!
hello world from cor_b!
8. async/await
>>>import asyncio

>>>async def cor():
>>>    print("start cor...")
>>>    r = await asyncio.sleep(5)
>>>    print("hello world!")

>>>loop = asyncio.get_event_loop()
>>>loop.run_until_complete(cor())
>>>loop.close()
start cor_a...
start cor_b...
hello world from cor_a!
hello world from cor_b!
9. asyncio.open_connection()
>>>import asyncio

>>>async def wget(host):
>>>    print('wget {}'.format(host))
>>>    connect = asyncio.open_connection(host,80)
>>>    reader,writer = await connect
>>>    header = "get / http/1.0\r\nHost: {}\r\n\r\n".format(host)
>>>    writer.write(header.encode())
>>>    await writer.drain()
>>>    async for line in reader:
>>>        print("{} header > {}".format(host,line.decode('unicode_escape').rstrip()))

>>>if __name__ == '__main__':
>>>    hosts = ['www.baidu.com','www.sina.com.cn']
>>>    wget_host = [wget(host) for host in hosts]
>>>    loop = asyncio.get_event_loop()
>>>    tasks = asyncio.wait(wget_host)
>>>    loop.run_until_complete(tasks)
>>>    loop.close()
wget www.baidu.com
wget www.sina.com.cn
www.baidu.com header > HTTP/1.1 400 Bad Request
www.baidu.com header > 
www.sina.com.cn header > HTTP/1.1 400 Bad Request
www.sina.com.cn header > Server: nginx
www.sina.com.cn header > Date: Mon, 23 Mar 2020 12:17:27 GMT
www.sina.com.cn header > Content-Type: text/html
www.sina.com.cn header > Content-Length: 150
www.sina.com.cn header > Connection: close
www.sina.com.cn header > X-Via-CDN: f=edge,s=cnc.jinan.union.69.nb.sinaedge.com,c=2408:8207:24a6:5651:d49e:2689:d3af:6c65;
www.sina.com.cn header > 
www.sina.com.cn header > <html>
www.sina.com.cn header > <head><title>400 Bad Request</title></head>
www.sina.com.cn header > <body>
www.sina.com.cn header > <center><h1>400 Bad Request</h1></center>
www.sina.com.cn header > <hr><center>nginx</center>
www.sina.com.cn header > </body>
www.sina.com.cn header > </html>

四、aiohttp包

>>>import asyncio
>>>from aiohttp import web

>>>async def index(request):
>>>    await asyncio.sleep(1)
>>>    return web.Response(body=b'<h1>Index</h1>')

>>>async def hello(request):
>>>    await asyncio.sleep(1)
>>>    text = '<h1>hello, %s!</h1>' % request.match._info['name']
>>>    return web.Response(body=text.encode('utf-8'))

>>>async def start_server(loop):
>>>    app = web.Application()
>>>    app.router.add_route('GET','/',index)
>>>    app.router.add_route('GET','/hello/{name}',hello)
>>>    srv = await loop.create_server(web.AppRunner(app),'127.0.0.1',12000)
>>>    print('Server started at http://127.0.0.1:12000...')
>>>    return srv

>>>if __name__ == '__main__':
>>>    loop = asyncio.get_event_loop()
>>>    loop.run_until_complete(start_server(loop))
>>>    loop.run_forever()

参考资料



本文作者:大师兄(superkmi)

上一篇下一篇

猜你喜欢

热点阅读