python 协程

77 阅读1分钟

前言:想要了解协程必须了解,迭代器,生成器,yield等含义

1.迭代器

可迭代的对象,可以是使用for遍历的对象,

案例: 实现一个可迭代对象并且判断对象是否是可迭代对象

#coding:utf-8
from collections import Iterable

class MyIterable(object):
    def __init__(self):
        self.names = list()
        self.index = 0
    def add(self,name):
        self.names.append(name)
    def __iter__(self):
        '''如果想让对象可以使用for 必须实现该方法'''
        return self
    def __next__(self):
        if self.index < len(self.names):
            ret = self.names[self.index]
            self.index += 1
            return ret
        else:
            raise StopIteration

if __name__ == '__main__':
    MyIter = MyIterable()
    MyIter.add("张三")
    MyIter.add("王五")
    MyIter.add("小六")
    print("判断是否为可迭代对象:",isinstance(MyIter,Iterable))
    for i in MyIter:
        print(i)

迭代器的应用:

斐波那契数列:

#coding:utf-8

class FibNac():
    def __init__(self,all_num):
        self.all_num = all_num
        self.current_num = 0
        self.a = 0
        self.b = 1
    def __iter__(self):
        return self
    def __next__(self):
        if self.current_num < self.all_num:
            ret = self.a
            self.a,self.b = self.b,self.a+self.b
            self.current_num += 1
            return ret
        else:
            raise StopIteration

if __name__ == '__main__':
    fib = FibNac(10)
    for i in fib:
        print(i)

python2中xrange,range的区别:xrange返回的不是一个值是一个实现值的方法,range返回一个空间很大的值,xrange更节省空间

2.生成器

保证函数中有yield一定会是一个生成器,生成器是一种特殊的迭代器

实现一个生成器:(next send功能一样都是唤醒程序,send可以传入参数)

#coding:utf-8

def main(num):
    a = 0
    cont = 0
    while cont < num:
        ret = yield a
        print("ret---->>>",ret)
        a += 1
        cont += 1
if __name__ == '__main__':
    obj= main(10)
    ret = next(obj)
    print(ret)
    ret = obj.send("hahah")
    print(ret)
    ret = next(obj)
    print(ret)

yield实现多任务并发:

#coding:utf-8
def task1():
    while True:
        print("task1")
        yield

def task2():
    while True:
        print("task2")
        yield
def main():
    while True:
        next(task1())
        next(task2())
if __name__  == "__main__":
    main()

greenlet,gevent可以实现多任务, gevent实现多线程:

#coding:utf-8
import time
import gevent

def f1(n):
    for i in range(n):
        print("f1:",gevent.getcurrent())
        gevent.sleep(0.5)
def f2(n):
    for i in range(n):
        print("f2:",gevent.getcurrent())
        gevent.sleep(0.5)

def main():
    print("-----------1")
    g1 = gevent.spawn(f1,5)
    print("-----------2")
    g2 = gevent.spawn(f2,8)
    print("-----------3")
    g1.join()
    g2.join()

if __name__ == '__main__':
    main()

执行结果:

image.png

协程依赖与线程 线程依赖于进程 协程切换开销最小

案例:

并发下载器

#coding:utf-8
import re
import urllib.request
import gevent
from bs4 import BeautifulSoup
import gzip
import os
import random
def downloader(img_name,img_url):
        req = urllib.request.urlopen(img_url)
        img_content  = req.read()
        with open(img_name,"wb") as f:
            f.write(img_content)

def get_url(url):
    urls = []
    req = urllib.request.urlopen(url).read()
    data = gzip.decompress(req).decode("utf-8")
    #print(data)
    soup = BeautifulSoup(data,'html.parser')
    images = soup.find_all('img')
    for item in images:
        urls.append(item.get('src'))
    #print(urls)
    return list(set(urls))

def main():
    img_url = get_url("https://www.douyu.com/g_yz")
    try:
        os.mkdir("img")
    except:
        pass
    for i in range(len(img_url[:20])):
        g1 = gevent.spawn(downloader,os.path.join("img","%d.png"%i),img_url[i])
        g2 = gevent.spawn(downloader,os.path.join("img","%d.png" %(i+1)),img_url[i+1])
        gevent.joinall([g1,g2])

if __name__ == '__main__':
    main()

优先考虑 协程然后线程 最后 进程