前言:想要了解协程必须了解,迭代器,生成器,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()
执行结果:
协程依赖与线程 线程依赖于进程 协程切换开销最小
案例:
并发下载器
#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()
优先考虑 协程然后线程 最后 进程