最近看到别人代码里面有这种方式,在编码下载歌词的时候想试试,查了资料,把样例放这里吧,理解了皮毛,照猫画虎。
1.协程的实现
协程(Coroutine),也可以被称为微线程,是一种用户态内的上下文切换技术。简而言之,其实就是通过一个线程实现代码块相互切换执行。例如:
def func1():
print(1)
... # 协程介入
print(2)
def func2():
print(3)
... # 协程介入
print(4)
func1()
func2()
上述代码是普通的函数定义和执行,按流程分别执行两个函数中的代码,并先后会输出:1、2、3、4
。但如果介入协程技术那么就可以实现函数见代码切换执行,最终输入:1、3、2、4
。
在Python3.4+可以通过以下:
- asyncio,在Python3.4中引入的模块用于编写协程代码;
- async & awiat,在Python3.5中引入的两个关键字,结合asyncio模块可以更方便的编写协程代码。
通过标准库的方式实现:asyncio是Python 3.4版本引入的标准库,直接内置了对异步IO的支持。
import asyncio
@asyncio.coroutine
def func1():
print(1)
yield from asyncio.sleep(2) # 遇到IO耗时操作,自动化切换到tasks中的其他任务
print(2)
@asyncio.coroutine
def func2():
print(3)
yield from asyncio.sleep(2) # 遇到IO耗时操作,自动化切换到tasks中的其他任务
print(4)
tasks = [
asyncio.ensure_future( func1() ),
asyncio.ensure_future( func2() )
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
通过关键字实现方法
async & await
关键字在Python3.5版本中正式引入,代替了asyncio.coroutine
装饰器,基于他编写的协程代码其实就是上一示例的加强版,让代码可以更加简便可读。
import asyncio
async def func1():
print(1)
await asyncio.sleep(2) # 耗时操作
print(2)
async def func2():
print(3)
await asyncio.sleep(2) # 耗时操作
print(4)
tasks = [
asyncio.ensure_future(func1()),
asyncio.ensure_future(func2())
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
2.案例:
2.1 同步实现案例
# requests库仅支持同步的http网络请求
import requests
def download_image(url):
print("开始下载:",url)
# 发送网络请求,下载图片
response = requests.get(url)
# 图片保存到本地文件
file_name = url.rsplit('_')[-1]
with open(file_name, mode='wb') as file_object:
file_object.write(response.content)
print("下载完成")
if __name__ == '__main__':
url_list = [
'https://www.1.jpg',
'https://www.2.jpg',
'https://www.3.jpg'
]
for item in url_list:
download_image(item)
2.2 基于协程的实现
# aiohttp 为支持异步编程的http请求库
import aiohttp
import asyncio
async def fetch(session, url):
print("发送请求:", url)
async with session.get(url, verify_ssl=False) as response:
content = await response.content.read()
file_name = url.rsplit('_')[-1]
with open(file_name, mode='wb') as file_object:
file_object.write(content)
async def main():
async with aiohttp.ClientSession() as session:
url_list = [
'https://www.1.jpg',
'https://www.2.jpg',
'https://www.3.jpg'
]
tasks = [asyncio.create_task(fetch(session, url)) for url in url_list]
await asyncio.wait(tasks)
if __name__ == '__main__':
asyncio.run(main())
2.3 下载歌词的部分代码
# aiohttp 为支持异步编程的http请求库
import aiohttp
import asyncio
async def fetch(session, song):
song_id = song['id']
song_name = song['name']
url = get_song_lyrics_url(song_id)
print("开始下载:", song_id, song_name, url)
async with session.get(url, verify_ssl=False) as response:
content = await response.content.read()
json_path = f_path + str(song_id) + "-" + song_name + '.json'
with open(json_path, mode='wb') as file_object:
file_object.write(content)
print(song_name, "下载完成:")
async def main():
async with aiohttp.ClientSession() as session:
tasks = [asyncio.create_task(fetch(session, song)) for song in songs]
await asyncio.wait(tasks)
await main()
# if __name__ == '__main__':
# asyncio.run(main())
发表回复