协程

协程最大的意义就是:利用了原来在等待延时操作的时间去切换任务执行。

一,迭代器

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

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

迭代一个对象的步骤为,判断对象是否为可迭代对象,若成立则调用iter(对象)方法并触发对象的__iter__方法的返回值(__iter__的返回值应该为一个迭代器),判断返回对象是否为一个迭代器,调用迭代器中的next(对象)方法并触发对象的__next__方法的返回值,若迭代器出现报错 StopIteration时,for循环将自动结束迭代,并不报错。

1,判断对象是否为可迭代对象:

在python中万物皆对象,只要在对象内部实现__iter__方法,则对象是可迭代对象,即可以使用for循环。

代码中判断:

from collections import Iterable

isinstance(对象名,Iterable)                # 返回布尔值进行判断

2,调用iter方法得到对象的__iter__的返回的返回值

在认定对象对应类中有__iter__后,调用iter方法,对象自动调用__iter__中return,return的返回值应该为一个迭代器。

3,判断返回对象是否为一个迭代器

迭代器中应有__iter__方法和__next__方法。调用next(对象)方法并触发对象中的__next__方法中的返回值。

代码中判断:

from collections import Iterator

isinstance(对象名,Iterator)                  # 返回布尔值进行判断

4,判断迭代器如何结束

当迭代器 raise StopIteration时,for循环语句将自动处理,正常结束循环。

5,为了更好的理解,附上代码

 

二,生成器

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

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

1,创建生成器的方法

  1)将列表推导式的[] 换成()

   eg: nums = (x*2 for x in range(10))

       next(nums)    /   for i in nums 

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

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

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

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

当调用next函数并触发__next__返回值无参数时,将自动报错StopIteration。多个生成器对象先后执行,相互无影响。

 

3,return 在 yield生成器中的作用

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

4,send

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

1)在传参上的区别:

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

send能接受yield传过来的参数,同时将send的参数传给yield处。

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

2)在调用上的区别:

next(生成器名)

生成器名.send(None)

三,协程-yield

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

 

四,协程-greenlet

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

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

五,协程-gevent

gevent 是对于greenlet的内部封装,而遇到延时就切换到下一个任务继续执行。

gevent只识别自己的框架延时,为了让gevent框架识别其他延时操作则需要转换

gevent在线程延时的时候才会自动执行,可使用join阻塞使gevent执行程序。

为方便理解,代码如下: 

import gevent
import time
from gevent import monkey

# 打补丁,让gevent框架识别耗时操作,比如:time.sleep,网络请求延时
monkey.patch_all()

# 任务1
def work1(num):
  for i in range(num):
    print("work1....")
    time.sleep(0.2)
    # gevent.sleep(0.2)

# 任务2
def work2(num):
  for i in range(num):
    print("work2....")
    time.sleep(0.2)
    # 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()

六,代码

import urllib.request
import gevent
from gevent import monkey
import re

url_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 list1

lists = ('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()

posted @ 2018-11-29 18:44  时间划过星空  阅读(176)  评论(0编辑  收藏  举报