本文共 6641 字,大约阅读时间需要 22 分钟。
迭代器最大的好处是:储存生成数据的方式,较储存生成数据的结果占用较少的内存。
若一个对象是可迭代对象,此对象不一定可以进行迭代;若一个对象是迭代器,则此对象一定可以进行迭代。迭代对象也可以同时为迭代器。
如何判断对象是否为可迭代对象呢?在python中万物皆对象,只要在对象内部实现__iter__方法,则对象是可迭代对象,即可以使用for循环。
代码中判断:from collections import Iterableisinstance(对象名,Iterable) # 返回布尔值进行判断
如何判断对象是否为迭代器对象呢?在认定对象为可迭代对象(对应类中有__iter__方法)后,调用iter方法,对象自动调用__iter__中return,return的返回值应该为一个迭代器。迭代器中应有__iter__方法和__next__方法。调用next(对象)方法并触发对象中的__next__方法中的返回值。若迭代器出现报错 StopIteration时,for循环将自动结束迭代,并不报错。
代码中判断:from collections import Iteratorisinstance(对象名,Iterator) # 返回布尔值进行判断
迭代一个对象的步骤为:
1)判断对象是否为可迭代对象(对象内部实现__iter__方法)。 2)调用iter方法并触发对象的__iter__方法的返回值(__iter__的返回值应该为一个迭代器)。 3)判断返回对象是否为一个迭代器(迭代器中应有__iter__方法和__next__方法。调用next(对象)方法并触发对象中的__next__方法中的返回值)。 4)判断迭代器如何结束(当迭代器 raise StopIteration时,for循环语句将自动处理,正常结束循环)。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可返回结果时暂定程序,而后继续执行。
生成器是一类特殊的迭代器, 它不需要再像上面的类一样写__iter__()和__next__()方法了,使用更加方便,它依然可以使用next函数和for循环取值,而当调用next函数并触发__next__返回值无参数时,将会自动报错StopIteration (迭代器无此功能) 。
nums = (x*2 for x in range(10)) next(nums) / for i in nums:
只要在def中有yield关键字的 就称为 生成器。在函数中只要有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)
当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...
因为send在应用于功能上于next相接近,在这里我们将send与next进行比较。
next只能接受yield传过来的参数。
send能接受yield传过来的参数,同时将send的参数传给yield处。 注意:send不能接收第一次的数据,因为程序从第一行执行,send的参数无处可传。但可以使用生成器名.send(None).next(生成器名)
生成器名.send(None)#生成器的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
在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()
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()
为方便理解,实现两示例代码如下:
示例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/