協(xié)程

迭代器

迭代是訪問集合元素的一種方式。迭代器是一個(gè)可以記住遍歷的位置的對象,迭代器對象從集合的第一個(gè)元素開始訪問,直到所有的元素被訪問完結(jié)束。迭代器只能往前不會(huì)后退。

可迭代對象

list,tuple,str,dict等類型的數(shù)據(jù)可以使用for ... in ... 的循環(huán)語法從其中依次拿到數(shù)據(jù)進(jìn)行使用,我們把這樣的過程稱為遍歷,也加迭代。

自己實(shí)現(xiàn)一個(gè)可以迭代的對象
# coding=utf-8
import time
from collections import Iterable
from collections import Iterator

class Classmate(object):
    def __init__(self):
        self.names = list()
        self.current_num = 0

    def add(self, name):
        self.names.append(name)

    def __iter__(self):
        ''' 如果想要一個(gè)對象可以迭代,那么必須實(shí)現(xiàn)__iter__方法 '''
        return self

    def __next__(self):
        if self.current_num < len(self.names):
            ret = self.names[self.current_num]
            self.current_num += 1
            return ret
        else:
            raise StopIteration

classmate = Classmate()
classmate.add("老王")
classmate.add("王二")
classmate.add("張三")

for name in classmate:
    print name
實(shí)現(xiàn)斐波拉契數(shù)列
# coding=utf-8

class FibIterator(object):
    '''斐波拉契數(shù)列迭代器'''
    def __init__(self, n):
        '''
        :param n: int, 指明生成數(shù)列的前n個(gè)數(shù)
        '''
        self.n = n
        # current 保存當(dāng)前生成到數(shù)列中的第幾個(gè)數(shù)
        self.current = 0
        # num1 保存前前一個(gè)數(shù),初始值為數(shù)列中的第一個(gè)數(shù) 0
        self.num1 = 0
        # num2 保存前一個(gè)數(shù),初始值是數(shù)列中的第二個(gè)數(shù) 1
        self.num2 = 1

    def __next__(self):
        ''' 被next()函數(shù)調(diào)用來獲取下一個(gè)數(shù) '''
        if self.current < self.n:
            num = self.num1
            self.num1, self.num2 = self.num2, sefl.num1+self.num2
            self.current += 1
            return num
        else:
            raise StopIteration

    def __iter__(self):
        ''' 迭代器的__iter__返回自身即可 ’‘’
        return self

if __name__ == "__main__":
    fib = FibIterator(10)
    for num in fib:
        print num
生成器

利用迭代器,我們可以在每次迭代獲取數(shù)據(jù)(通過next()方法)時(shí)按照特定的規(guī)律進(jìn)行生成。但是我們在實(shí)現(xiàn)一個(gè)迭代器時(shí),關(guān)于當(dāng)前迭代到的狀態(tài)需要自己記錄,進(jìn)而才能根據(jù)當(dāng)前狀態(tài)生成下一個(gè)數(shù)據(jù)。為了達(dá)到記錄當(dāng)前狀態(tài),并配合next()函數(shù)進(jìn)行迭代使用,我們可以采用更加簡便的方法---生成器。
生成器是一種特殊的迭代器

創(chuàng)建生成器方法1

要?jiǎng)?chuàng)建一個(gè)生成器,有很多方法。第一種方法,只要把一個(gè)列表生成式的[]改為()

L = [x*2 for x in range(5)]
print L
>>> [0, 2, 4, 6, 8]
G = (x*2 for x in range(5))
print G
>>> <generator object <genexpr> at 0x7f626c123db0>
# 創(chuàng)建L和G的區(qū)別僅在于最外層的[]和(),L是一個(gè)列表,而G是一個(gè)生成器。
# 我們可以直接打印列表L中每一個(gè)元素,而對于生成器G。我們可以按照迭代器的使用方式來使用。
# 即可以通過next()函數(shù),for循環(huán),list()等方法來使用。
next(G)
>>> 0
next(G)
>>> 2
next(G)
>>> 4  
創(chuàng)建生成器方法2

generator 非常強(qiáng)大。如果推算的算法比較復(fù)雜,用類似列表生成式的for循環(huán)無法實(shí)現(xiàn)的時(shí)候,還可以使用函數(shù)來實(shí)現(xiàn)。

def create_num(all_num):
    a, b = 0, 1
    current_num = 0
    while current_num < all_num:
        # print a
        yield a # 如果一個(gè)函數(shù)中有yield語句,那么就不函數(shù),而是一個(gè)生成器模板
        a, b = b,  a + b
        current_num += 1

# 如果在調(diào)用 create_num 的時(shí)候,發(fā)現(xiàn)這個(gè)函數(shù)中有yield那么此時(shí),不是調(diào)用函數(shù),而是創(chuàng)建一個(gè)生成器對象。
obj = create_num(10)
print next(obj)
print next(obj)
for num in obj:
    print num
# =====================================================
def create_num(all_num):
    a, b = 0, 1
    current_num = 0
    while current_num < all_num:
        yield a 
        a, b = b,  a + b
        current_num += 1
    return "ok...."

obj2 = create_num(10)
while True:
    try:
        ret = next(obj2)
        print ret
    except E  xception as ret:
        print ret.value # 獲取 return 的值
        break
# 使用 send ==================================
def create_num(all_num):
    a, b = 0, 1
    current_num = 0
    while current_num < all_num:
        ret = yield a 
        print ret # 這里會(huì)打印 hahahaha
        a, b = b,  a + b
        current_num += 1
    return "ok...."

obj3 = create_num(10)
# obj3.send(None) # send 一般不會(huì)放到第一次啟動(dòng)生成器,如果非要這么做,那么只能傳遞 None
ret = next(obj)
print ret
# send 里面的數(shù)據(jù)會(huì)傳遞給 上面的 ret,當(dāng)做yield的結(jié)果,然后ret保存這個(gè)結(jié)果
# send 的結(jié)果是下一次調(diào)用yield時(shí),yield 左邊的值
ret = obj,send(“hahahaha”)
print ret
yield 實(shí)現(xiàn)多任務(wù)
import time

def task_1():
    while True:
        print "11111"
        time.sleep(0.1)
        yield

def task_2():
    while True:
        print "22222"
        time.sleep(0.1)
        yield

def main():
    t1 = task_1()
    t2 = task_2()
    # 先讓t1執(zhí)行一會(huì),當(dāng)遇到y(tǒng)ield的時(shí)候,返回回來
    # 再執(zhí)行t2,當(dāng)t2遇到y(tǒng)ield的時(shí)候,再次切換到t1
    # 這樣 t1/t2/t1/t2 交替執(zhí)行,最終實(shí)現(xiàn)了多任務(wù)----協(xié)程
    while True:
        next(t1)
        next(t2)

if __nam__ == "__main__":
    main()
使用 greenlet
import time
from greenlet import greenlet

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 中運(yùn)行
gr1.switch()
gevent 使用

greenlet 已經(jīng)實(shí)現(xiàn)了協(xié)程,但是這個(gè)是人工切換,太麻煩。Python還有一個(gè)更加強(qiáng)大的模塊 gevent
其原理是當(dāng)一個(gè) greenlet 遇到IO(指的是 input, output, 比如網(wǎng)絡(luò),文件操作等)操作時(shí),比如訪問網(wǎng)絡(luò),就自動(dòng)切換到其他的greenlet,等到IO操作完成,再在適當(dāng)?shù)臅r(shí)候切換回來繼續(xù)執(zhí)行。
由于IO操作非常耗時(shí),經(jīng)常使程序處于等待狀態(tài),有了gevent為我們自動(dòng)切換協(xié)程,就保證總有 greenlet在運(yùn)行,而不是等待IO。

import time
import gevent

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)

g1 = gevent.spawn(f1, 5)
g2 = gevent.spawn(f2, 5)
g3 = gevent.spawn(f3, 5)
g1.join()
g2.join()
g3.join()

# ================================================
import time
import gevent
import random
from gevent import monkey

# 有耗時(shí)操作時(shí)需要
monkey.patch_all() # 將程序中用到的耗時(shí)操作代碼,換為gevent中自己實(shí)現(xiàn)的模塊

def corourine_work(coroutine_name):
    for i in range(10):
        print coroutine_name, i
        time.sleep(random.random())

gevent.joinall([
    gevent.spawn(coroutine_work, "work1"),
    gevent.spawn(coroutine_work, "work2")
])
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一、總體內(nèi)容 1.1、協(xié)程的介紹 1.2、迭代器以及迭代器的應(yīng)用 1.3、生成器(生成器與迭代器保存的都是生成數(shù)據(jù)...
    IIronMan閱讀 900評(píng)論 0 1
  • python之進(jìn)程、線程與協(xié)程 有這么個(gè)例子說他們的區(qū)別,幫助理解很有用。 有一個(gè)老板想開一個(gè)工廠生產(chǎn)手機(jī)。 他需...
    道無虛閱讀 3,335評(píng)論 0 3
  • 目錄:一、基于生成器的協(xié)程二、協(xié)程狀態(tài)三、協(xié)程預(yù)激裝飾器四、終止協(xié)程和異常處理五、協(xié)程返回值六、yield fro...
    Recalcitrant閱讀 472評(píng)論 0 0
  • 協(xié)程是什么? 協(xié)程是python個(gè)中另外一種實(shí)現(xiàn)多任務(wù)的方式,只不過比線程更小占用更小執(zhí)行單元(理解為需要的資源)...
    沉吟不語閱讀 429評(píng)論 0 0
  • 01182a35a7fc閱讀 130評(píng)論 0 0

友情鏈接更多精彩內(nèi)容