协程

迭代器

迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象,迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。

可迭代对象

list,tuple,str,dict等类型的数据可以使用for ... in ... 的循环语法从其中依次拿到数据进行使用,我们把这样的过程称为遍历,也加迭代。

自己实现一个可以迭代的对象
# 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):
        ''' 如果想要一个对象可以迭代,那么必须实现__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
实现斐波拉契数列
# coding=utf-8

class FibIterator(object):
    '''斐波拉契数列迭代器'''
    def __init__(self, n):
        '''
        :param n: int, 指明生成数列的前n个数
        '''
        self.n = n
        # current 保存当前生成到数列中的第几个数
        self.current = 0
        # num1 保存前前一个数,初始值为数列中的第一个数 0
        self.num1 = 0
        # num2 保存前一个数,初始值是数列中的第二个数 1
        self.num2 = 1

    def __next__(self):
        ''' 被next()函数调用来获取下一个数 '''
        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
生成器

利用迭代器,我们可以在每次迭代获取数据(通过next()方法)时按照特定的规律进行生成。但是我们在实现一个迭代器时,关于当前迭代到的状态需要自己记录,进而才能根据当前状态生成下一个数据。为了达到记录当前状态,并配合next()函数进行迭代使用,我们可以采用更加简便的方法---生成器。
生成器是一种特殊的迭代器

创建生成器方法1

要创建一个生成器,有很多方法。第一种方法,只要把一个列表生成式的[]改为()

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>
# 创建L和G的区别仅在于最外层的[]和(),L是一个列表,而G是一个生成器。
# 我们可以直接打印列表L中每一个元素,而对于生成器G。我们可以按照迭代器的使用方式来使用。
# 即可以通过next()函数,for循环,list()等方法来使用。
next(G)
>>> 0
next(G)
>>> 2
next(G)
>>> 4  
创建生成器方法2

generator 非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以使用函数来实现。

def create_num(all_num):
    a, b = 0, 1
    current_num = 0
    while current_num < all_num:
        # print a
        yield a # 如果一个函数中有yield语句,那么就不函数,而是一个生成器模板
        a, b = b,  a + b
        current_num += 1

# 如果在调用 create_num 的时候,发现这个函数中有yield那么此时,不是调用函数,而是创建一个生成器对象。
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 # 这里会打印 hahahaha
        a, b = b,  a + b
        current_num += 1
    return "ok...."

obj3 = create_num(10)
# obj3.send(None) # send 一般不会放到第一次启动生成器,如果非要这么做,那么只能传递 None
ret = next(obj)
print ret
# send 里面的数据会传递给 上面的 ret,当做yield的结果,然后ret保存这个结果
# send 的结果是下一次调用yield时,yield 左边的值
ret = obj,send(“hahahaha”)
print ret
yield 实现多任务
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执行一会,当遇到yield的时候,返回回来
    # 再执行t2,当t2遇到yield的时候,再次切换到t1
    # 这样 t1/t2/t1/t2 交替执行,最终实现了多任务----协程
    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 中运行
gr1.switch()
gevent 使用

greenlet 已经实现了协程,但是这个是人工切换,太麻烦。Python还有一个更加强大的模块 gevent
其原理是当一个 greenlet 遇到IO(指的是 input, output, 比如网络,文件操作等)操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。
由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有 greenlet在运行,而不是等待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

# 有耗时操作时需要
monkey.patch_all() # 将程序中用到的耗时操作代码,换为gevent中自己实现的模块

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")
])
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 198,030评论 5 464
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,198评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 144,995评论 0 327
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,973评论 1 268
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,869评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,766评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,967评论 3 388
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,599评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,886评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,901评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,728评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,504评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,967评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,128评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,445评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,018评论 2 343
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,224评论 2 339

推荐阅读更多精彩内容

  • 一、总体内容 1.1、协程的介绍 1.2、迭代器以及迭代器的应用 1.3、生成器(生成器与迭代器保存的都是生成数据...
    IIronMan阅读 861评论 0 1
  • python之进程、线程与协程 有这么个例子说他们的区别,帮助理解很有用。 有一个老板想开一个工厂生产手机。 他需...
    道无虚阅读 3,161评论 0 3
  • 目录:一、基于生成器的协程二、协程状态三、协程预激装饰器四、终止协程和异常处理五、协程返回值六、yield fro...
    Recalcitrant阅读 370评论 0 0
  • 协程是什么? 协程是python个中另外一种实现多任务的方式,只不过比线程更小占用更小执行单元(理解为需要的资源)...
    沉吟不语阅读 363评论 0 0
  • 01182a35a7fc阅读 109评论 0 0