迭代器--》生成器--》協程的關係與區別

 

一、迭代器(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核數不同 多是並行的, 可是協程是在一個線程中, 因此是併發的!
相關文章
相關標籤/搜索