迭代器--》生成器--》协程的关系与区别

 

一、迭代器(iterator)python

        是一个实现了迭代器协议的对象,python的一些内置数据类型(列表,数组,字符串,字典等)均可以经过for语句进行迭代,咱们也能够本身建立一个容器,实现了迭代器协议,能够经过for,next方法进行迭代,在迭代的末尾,会引起stopIteration异常。数组

判断xxx_obj是否能够迭代

在第1步成立的前提下,调用 iter 函数获得 xxx_obj 对象的 __iter__ 方法的返回值

__iter__ 方法的返回值是一个迭代器

若是想要一个对象称为一个 能够迭代的对象,便可以使用for,必须实现 __iter__方法

__iter__ 中必须返回对象的引用【要这个对象有__iter__和__next__方法, 实际上取的__next__的返回值】

迭代器结束,须要抛出一个 StopIteration 异常。

二、生成器(generator)【一种特殊迭代器网络

        1)是经过yield语句快速生成迭代器,能够不用iter和next方法 ;多线程

        2)yield可使一个普通函数变成一个生成器,而且相应的next()方法返回是yield后的值。一种更直观的解释是:程序执行到yield时会返回结果并暂停,再次调用next时会从上次暂停的地方继续开始执行并发

        3)生成器自身有构成一个迭代器,每次迭代时使用一个yield返回 的值,一个生成器中能够有多个yield的值函数

        4)建立生成器的方法:url

              a、()spa

    

 

               b、yield    操作系统

def create_num(all_num):
    # a = 1
    # b = 1
    a, b = 0, 1
    current_num = 0
    while current_num < all_num:
        yield a              # 若是一个函数中有yield语句,那么这个就不在是函数,而是一个生成器的模板
        a, b = b, a + b
        current_num += 1
 
 
if __name__ == '__main__':
    # 若是在调用create_num的时候,发现这个函数有yield,此时不是调用函数,而是建立一个生成器对象
    obj = create_num(10)
    for num in obj:
        print(num)

        五、send使用--启动生成器线程

def create_num(all_num):
    a, b = 0, 1
    current_num = 0
    while current_num < all_num:
        res = yield a
        print(">>>>ret>>>>", res)
        a, b = b, a + b
        current_num += 1
 
 
if __name__ == '__main__':
    obj = create_num(4)
    # obj.send(None) # send通常不会放到第一次启动生成器,若是非要如此,传递None
    ret = next(obj)
    print(ret)
    ret = obj.send("hhhhh")
    print(ret)
    # send里面的数据,会传递给第5行,看成yield a的结果,而后res保存这个结果..
    # send的结果是下一次调用yield时,yield后面的值
    ret = obj.send(None)
    print(ret)
    ret = obj.send(None)
    print(ret)

       六、yieldreturn区别

             yield能够暂停函数执行,且下一次执行时候恢复

三、迭代器和生成器做用

  • 迭代器: 减小内存空间, 能实现循环
  • 生成器: 能让一个函数看上去能暂停执行
  • 都是保证生成数据代码, 不是保存结果

四、多任务-协程(yield执行)  

  a、生成器函数其实就是实现了协程,即便用yield, send(), next()实现协程

  b、进程占资源最多, 其次线程, 协程占资源最少!

  

#!/bin/python3
# -*- coding=utf-8 -*-
 
import time
def task_1():
    while True:
        print("------1-------")
        time.sleep(0.1)
        yield 
 
 
def task_2():
    while True:
        print("------2------")
        time.sleep(0.2)
        yield 
 
 
def main():
   t1 = task_1()
   t2 = task_2()
   while True:
       next(t1)
       next(t2)
if __name__ == "__main__":
    main()

  并行: 有两个任务, 可是有四个CPU的核, 一个任务占一个核, 每一个都在作

  并发: 有不少任务, 可是只有两个核, 因此 交替执行

  4.一、greenlet实现多任务(核心仍是yield)

           

#!/bin/python3
# -*- encoding=utf-8 -*-
 
from greenlet import greenlet
import time
def test1():
    while True:
        print("----A----")
        gr2.switch()
        time.sleep(0.5)
def test2():
    while True:
        print("----B----")
        gr1.switch()
        time.sleep(0.5)
gr1 = greenlet(test1)
gr2 = greenlet(test2)
# 切换到gr1中执行
gr1.switch()

  4.二、gevent实现协程(更强大,经常使用)

    

#!/bin/python3
# -*-encoding=utf-8-*-
 
import gevent
import time 
def f1(n):
    for i in range(n):
        print(gevent.getcurrent(), i)
        gevent.sleep(0.5)
def f2(n):
    for i in range(n):
        print(gevent.getcurrent(), i)
        gevent.sleep(0.5)
def f3(n):
    for i in range(n):
        print(gevent.getcurrent(), i)
        gevent.sleep(0.5)
print("----1-----")
g1 = gevent.spawn(f1, 5)
print("----2-----")
g2 = gevent.spawn(f2, 5)
print("----3-----")
g3 = gevent.spawn(f3, 5)
print("----4-----")
g1.join()
g2.join()
g3.join()

gevent遇到延时操做就切换, 利用了等待耗时的操做, 去作其余的事情

以下: 加入monkey.patch_all()则无须将 time.sleep()改为 gevent.sleep()

#!/bin/python3
# -*-encoding=utf-8-*-
 
import gevent
from gevent import monkey 
import time 
monkey.patch_all()
def f1(n):
    for i in range(n):
        print(gevent.getcurrent(), i)
        time.sleep(0.5)
#        gevent.sleep(0.5)
 
def f2(n):
    for i in range(n):
        print(gevent.getcurrent(), i)
        time.sleep(0.5)
 #       gevent.sleep(0.5)
 
def f3(n):
    for i in range(n):
        print(gevent.getcurrent(), i)
        time.sleep(0.5)
#        gevent.sleep(0.5)
 
print("----1-----")
g1 = gevent.spawn(f1, 5)
print("----2-----")
g2 = gevent.spawn(f2, 5)
print("----3-----")
g3 = gevent.spawn(f3, 5)
print("----4-----")
g1.join()
g2.join()
g3.join()

以下: 将须要join的代码, 写成列表, 简洁

#!/bin/python3
# -*-encoding=utf-8-*-
 
import gevent
from gevent import monkey 
import time 
monkey.patch_all()
def f1(n):
    for i in range(n):
        print(gevent.getcurrent(), i)
        time.sleep(0.5)
#        gevent.sleep(0.5)
 
def f2(n):
    for i in range(n):
        print(gevent.getcurrent(), i)
        time.sleep(0.5)
 #       gevent.sleep(0.5)
 
def f3(n):
    for i in range(n):
        print(gevent.getcurrent(), i)
        time.sleep(0.5)
#        gevent.sleep(0.5)
 
gevent.joinall([
    gevent.spawn(f1, 5), 
    gevent.spawn(f2, 5), 
    gevent.spawn(f3, 5)
    ])  

  4.3并发下载器

#!/bin/python3
#-*- encoding=utf-8 -*-
 
import gevent 
import urllib.request 
from gevent import monkey
monkey.patch_all()
def downloader(img_name, img_url):
    req = urllib.request.urlopen(img_url)
    img_content = req.read()
      
    with open("./img/"+ img_name, "wb") as f:
        f.write(img_content)
def main():
    gevent.joinall([
            gevent.spawn(downloader, "1.jpg", 'https://rpic.douyucdn.cn/asrpic/190417/5440020_3968619_65b10_2_2142.jpg'),
            gevent.spawn(downloader, '2.png', "https://rpic.douyucdn.cn/asrpic/190417/594613_2143.png")
        ])  
if __name__=="__main__":
    main()

五、进程/线程/协程对比

  • 进程: 耗费资源最多, 进程里必定有一个线程, 默认线程称为主线程。进程是资源分配的单位。(最稳定, 耗费资源最多)

  • 线程线程是操做系统调度的单位. 线程切换须要的资源通常, 效率通常 (不考虑GIL状况) 

  • 协程: 协程切换任务资源很小, 效率高;

    • 特色: 在等待某个资源到来 期间, 去执行其余代码....多线程里有不少网络堵塞, 推荐先用协程 !

  • 多进程、多线程根据cpu核数不同 多是并行的, 可是协程是在一个线程中, 因此是并发的!
相关文章
相关标签/搜索