首先开发一个简单的实现是正常的。因此,例如,我们可能从一个非并行程序开始,然后添加并发性。我希望能够平稳地来回切换。
例如,单线程(伪代码):
results=[]
for url in urls:
# This then calls other functions that call yet others
# in a call hierarchy, down to a requests.request() call.
get_result_and_store_in_database(url)
异步(伪代码):
# The following calls other functions that call yet others
# in a call hierarchy, down to an asyncio ClientSession().get() call.
# It runs HTTP requests and store the results in a database.
# The multiple URLs are processed concurrently.
asyncio.run(get_results_in_parallel_and_store_in_db(urls))
使用Python async/await
,通常用asyncio.run()
包装运行(与普通程序中使用的循环相比);然后在调用层次结构的底部,您使用aiohttp.ClientSession().get(url)
之类的IO操作(与普通requests.request()
相比)。
但是,在异步版本中,这两者之间的调用层次结构中的所有函数都必须写为async/await
。因此,我需要编写两个基本相同的呼叫层次结构的副本,主要区别在于它们是否具有async/await
关键字。
很多代码重复。
如何制作可切换的非并发/异步程序?
答案 0 :(得分:1)
这确实是一个大话题,但不是一般话题。我个人有一个私有的WebDAV项目,该项目同时实现了同步版本和异步版本。
首先,我的WebDAV客户端接受一个名为client
的参数,它可以是requests.Session
或aiohttp.ClientSession
来执行同步请求或异步请求。
第二,我有一个基类来实现所有常见的逻辑,例如:
def _perform_dav_request(self, method, auth_tuple=None, client=None, **kwargs):
auth_tuple = self._get_auth_tuple(auth_tuple)
client = self._get_client(client)
data = kwargs.get("data")
headers = None
url = None
path = kwargs.get("path")
if path:
root_url = urljoin(self._base_url, self._dav_url)
url = root_url + path
from_path = kwargs.get("from_path")
to_path = kwargs.get("to_path")
if from_path and to_path:
root_url = urljoin(self._base_url, self._dav_url)
url = root_url + from_path
destination = root_url + quote(to_path)
headers = {
"Destination": destination
}
return client.request(method, url, data=data, headers=headers, auth=auth_tuple)
事实是requests.Session
和aiohttp.ClientSession
都支持几乎相同的API,因此在这里我可以使用模糊调用client.request(...)
。
第三,我必须导出不同的API:
# In async client
async def ls(self, path, auth_tuple=None, client=None):
response = await self._perform_dav_request("PROPFIND", auth_tuple, client, path=path)
if response.status == 207:
return parse_ls(await response.read())
raise WebDavHTTPError(response.status, await response.read())
# In sync client
def ls(self, path, auth_tuple=None, client=None):
response = self._perform_dav_request("PROPFIND", auth_tuple, client, path=path)
if response.status_code == 207:
return parse_ls(response.content)
raise WebDavHTTPError(response.status_code, response.content)
所以最终我的用户可以像dav = DAV(...)
或dav = AsyncDAV(...)
那样使用它。
这就是我处理两个不同版本的方式。我认为,您可以通过函数调用传递这些协程,并且仅在最高级别进行评估。因此,您只需要在最后一级编写不同的代码,而在所有其他级别上具有相同的逻辑即可。