博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
python之迭代器(Iterator)、生成器(yield)、协程(gevent)
阅读量:4202 次
发布时间:2019-05-26

本文共 6641 字,大约阅读时间需要 22 分钟。

一,迭代器

迭代器最大的好处是:储存生成数据的方式,较储存生成数据的结果占用较少的内存

若一个对象是可迭代对象,此对象不一定可以进行迭代;若一个对象是迭代器,则此对象一定可以进行迭代。迭代对象也可以同时为迭代器。

1,可迭代对象

如何判断对象是否为可迭代对象呢?在python中万物皆对象,只要在对象内部实现__iter__方法,则对象是可迭代对象,即可以使用for循环。

代码中判断:

from collections import Iterableisinstance(对象名,Iterable)                # 返回布尔值进行判断
2,迭代器

如何判断对象是否为迭代器对象呢?在认定对象为可迭代对象(对应类中有__iter__方法)后,调用iter方法,对象自动调用__iter__中return,return的返回值应该为一个迭代器。迭代器中应有__iter__方法和__next__方法。调用next(对象)方法并触发对象中的__next__方法中的返回值。若迭代器出现报错 StopIteration时,for循环将自动结束迭代,并不报错。

代码中判断:from collections import Iteratorisinstance(对象名,Iterator)                  # 返回布尔值进行判断
3,总结

迭代一个对象的步骤为:

1)判断对象是否为可迭代对象(对象内部实现__iter__方法)。
2)调用iter方法并触发对象的__iter__方法的返回值(__iter__的返回值应该为一个迭代器)。
3)判断返回对象是否为一个迭代器(迭代器中应有__iter__方法和__next__方法。调用next(对象)方法并触发对象中的__next__方法中的返回值)。
4)判断迭代器如何结束(当迭代器 raise StopIteration时,for循环语句将自动处理,正常结束循环)。

4,为了更好的理解,附上代码
from collections import Iterable,Iteratorclass Classmate():    def __init__(self):        self.names = list()        self.current_num = 0            def add(self,name):        self.names.append(name)        def __iter__(self):        """只要在对象内部实现__iter__方法,        则对象是可迭代对象,即可以使用for循环"""        return self  # 可迭代对象可以返回自己作为迭代器,但是要保证实现了__iter__、__next__方法。        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 # 当迭代器报错StopIteration时,for循环语句将自动处理,正常结束循环。            classmate = Classmate()classmate.add("老大")classmate.add("老二")classmate.add("老三")print("判断classmate是否是可以迭代对象:",isinstance(classmate,Iterable))# classmate_iterator = iter(classmate)print("判断classmate_iterator是否是迭代器:",isinstance(classmate_iterator,Iterator))print(next(classmate))for name in classmate:    print(name)

二,生成器

生成器的一大好处是:储存生成数据的方式。较return可返回结果时暂定程序,而后继续执行。

1,什么是生成器

生成器是一类特殊的迭代器, 它不需要再像上面的类一样写__iter__()和__next__()方法了,使用更加方便,它依然可以使用next函数和for循环取值,而当调用next函数并触发__next__返回值无参数时,将会自动报错StopIteration (迭代器无此功能)

2,创建生成器的方法
1)将列表推导式的[] 换成()
nums = (x*2 for x in range(10))       next(nums)    /   for i in nums:
2) 函数创建生成器

只要在def中有yield关键字的 就称为 生成器。在函数中只要有yield,那个这就不是一个函数,而是一个生成器模板,在调用函数时就是在创建一个生成器对象。

3,yield生成器在代码中如何执行

当代码执行到yield语句时,将暂停程序,并返回数据,依然可以使用next函数和for循环取值。

当调用next函数并触发__next__返回值无参数时,将自动报错StopIteration。多个生成器对象先后执行,相互无影响。
为便于理解,附上实现 “斐波那契”生成器实例:

def create_num(all_num):    a,b = 0,1    current_num = 0    while current_num < all_num:        yield a  # 如果一个函数中有yield语句,那么这个就不在是函数,而是一个生成器的模板。        a,b = b,a+b        current_num += 1 # 如果在调用create_num的时候,发现这个函数中有yield那么此时,不是调用函数,而是创建一个生成器对象。obj = create_num(10)obj2 = create_num(10)# ret = next(obj)# print("obj:",ret)# ret = next(obj2)# print("obj2:",ret)for index in obj:    print(index)for index in obj2:    print(index)
3,return 在 yield生成器中的作用

当yield生成器报错时,(Exception的错误原因.value)将返回return数据,如下所示:

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     return "OK..."obj = create_num(5)while True :     try:        ret = next(obj)        print(ret)    except Exception as e:        print(e,e.value)        break#---output--------------01123OK... OK...
4,send

因为send在应用于功能上于next相接近,在这里我们将send与next进行比较。

1)在传参上的区别:

next只能接受yield传过来的参数。

send能接受yield传过来的参数,同时将send的参数传给yield处。
注意:send不能接收第一次的数据,因为程序从第一行执行,send的参数无处可传。但可以使用生成器名.send(None).

2)在调用上的区别:

next(生成器名)

生成器名.send(None)

3)通过实例:
#生成器的send用法 generator.send(value)def test():    i = 1    while i < 5:        temp = yield i**2        print("temp value is : %s "%temp)        i += 1t = test()#第一次运行只能使用next或者send(None)# print(t.__next__())print(next(t)) #send的作用相当于使生成器继续运行,并且传递的参数为yield的返回值(程序中即temp的值)print(t.send("Hello World"))print(next(t))    #相当于send(None) 此时temp = None#---output--------------1temp value is : Hello World 4temp value is : None 9

三,协程

1,yield

在yield生成器中,利用next()方法,实现协程的多任务。遇到延时将等待延时,不切换任务。

import time def task_1():    while True:        print("-----1-----")        time.sleep(1)        yield        def task_2():    while True:        print("-----2-----")        time.sleep(1)        yielddef main():    t1 = task_1()    t2 = task_2()    # 先让t1运行一会儿,当t1中遇到yield的时候,再返回到next(t1)处,然后再执行t2,    # 当他遇到yield的时候,再次切换到t1中。这样t1、t2、t1...的交替运行,最终实现了多任务。。协程    while True:        next(t1)        next(t2)if __name__ == "__main__":    main()
2,greenlet

greenlet 是对于yield的一个简单的封装,将yield利用next控制线程执行替换成了自动执行(像穿针引线)。遇到延时将等待延时,不切换任务。

实现原理与yield近乎一样,只是更加方便了。

import time import greenlet# 任务1def work1():    for i in range(5):        print("work1...")        time.sleep(1)        # 切换到协程2里面执行对应的任务        g2.switch()# 任务2        def work2():    for i in range(5):        print("work2...")        time.sleep(1)        # 切换到第一个协程执行对应的任务        g1.switch()if __name__ == "__main__":    # 创建协程制定对应的任务    g1 = greenlet.greenlet(work1)    g2 = greenlet.greenlet(work2)    g1.switch()
3,gevent
  • gevent 是对于greenlet的内部封装,而遇到延时就切换到下一个任务继续执行。
  • gevent只识别自己的框架延时,为了让gevent框架识别其他延时操作则需要转换。
  • gevent在线程延时的时候才会自动执行,可使用join阻塞使gevent执行程序。

为方便理解,实现两示例代码如下:

示例1:

import geventimport timefrom gevent import monkey# 打补丁,让gevent框架识别耗时操作,比如:time.sleep,网络请求延时monkey.patch_all()# 任务1def work1(num):    for i in range(num):        print("work1....")        time.sleep(1)        # gevent.sleep(0.2)# 任务2def work2(num):    for i in range(num):        print("work2....")        time.sleep(1)        # gevent.sleep(0.2)def main():    gevent.joinall([        gevent.spawn(work1, 5),        gevent.spawn(work2, 5)    ])    print('end')    # 创建协程指定对应的任务    # g1 = gevent.spawn(work1, 6)    # g2 = gevent.spawn(work2, 6)    # while True:    #     # print("主线程中执行")    #     time.sleep(1)'if __name__ == '__main__':    main()

示例2:

import urllib.requestimport geventfrom gevent import monkeyimport reurl_list = list()monkey.patch_all()# def get_url():#   files = open('cat_url.txt','r').read()#   list1 = re.findall(r'http://img0.*?\.jpg',files,re.S)#    if list1:#     print(list1,len(list1))#   else:#      print('no match')#    return list1lists = ('http://img06.tooopen.com/images/20180916/tooopen_sl_21540854823136.jpg','http://img06.tooopen.com/images/20180911/tooopen_sl_145217521750944.jpg',  'http://img08.tooopen.com/20181010/tooopen_sl_20520752729360.jpg')def downloader(img_name, img_url):  req = urllib.request.urlopen(img_url)  img_content = req.read()  with open('./test_cat/'+img_name, "wb") as f:    f.write(img_content)def main():  for index in range(len(lists)):    url_list.append(gevent.spawn(downloader,'cat'+str(index)+'.jpg',lists[index]))  gevent.joinall(url_list)if __name__ == '__main__':  main()

转载地址:http://jsili.baihongyu.com/

你可能感兴趣的文章
NetBeans IDE 中国教育考试版 (2007)运行界面空白解决方法(计算机二级java)
查看>>
全国计算机等级考试 二级java官方教材 纠错勘误 (非官方)
查看>>
PhotoshopCC 使用透视剪裁工具时提示:无法使用透视剪裁工具因为图像包含不受支持的图层类型?
查看>>
LeetCode-栈|双指针-42. 接雨水
查看>>
stdin,stdout,stderr详解
查看>>
Linux文件和设备编程
查看>>
文件描述符
查看>>
终端驱动程序:几个简单例子
查看>>
登录linux密码验证很慢的解决办法
查看>>
fcntl函数总结
查看>>
HTML条件注释
查看>>
Putty远程服务器的SSH经验
查看>>
内核态与用户态
查看>>
使用mingw(fedora)移植virt-viewer
查看>>
趣链 BitXHub跨链平台 (4)跨链网关“初介绍”
查看>>
C++ 字符串string操作
查看>>
C++ qsort 与 sort
查看>>
win10配置tensorflow1.14,1.15,2.0缺少cudart64_100.dll
查看>>
在Linux下搭建带MOD 我的世界(Minecraft)服务器
查看>>
react 复制antd表格行
查看>>