Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/293.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何编写一个Python程序的异步/同步变体?_Python_Python 3.x_Async Await_Python Asyncio - Fatal编程技术网

如何编写一个Python程序的异步/同步变体?

如何编写一个Python程序的异步/同步变体?,python,python-3.x,async-await,python-asyncio,Python,Python 3.x,Async Await,Python Asyncio,首先开发一个简单的实现是正常的。例如,我们可以从一个非并发程序开始,然后添加并发性。我希望能够顺利地来回切换 例如,单线程(伪代码): 异步(伪代码): 使用Pythonasync/await,通常使用asyncio.run()包装运行(与普通程序中使用的循环相比);然后在调用层次结构的底部,使用IO操作,如aiohttp.ClientSession().get(url)(与普通的requests.request()) 但是,在异步版本中,这两者之间的调用层次结构中的所有函数都必须写成async

首先开发一个简单的实现是正常的。例如,我们可以从一个非并发程序开始,然后添加并发性。我希望能够顺利地来回切换

例如,单线程(伪代码):

异步(伪代码):

使用Python
async/await
,通常使用
asyncio.run()
包装运行(与普通程序中使用的循环相比);然后在调用层次结构的底部,使用IO操作,如
aiohttp.ClientSession().get(url)
(与普通的
requests.request()

但是,在异步版本中,这两者之间的调用层次结构中的所有函数都必须写成
async/await
。因此,我需要编写基本相同的调用层次结构的两个副本,主要区别在于它们是否具有
async/await
关键字

这是大量的代码重复


如何制作一个可切换的非并发/异步程序?

这确实是一个很大的话题,但不是一般的话题。我个人有一个私有的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(…)
那样使用它


这就是我处理两个不同版本的方式。我认为这个想法是,您可以通过函数调用传递这些协程,并且只在最高级别对它们进行评估。因此,您只需要在最后一个级别编写不同的代码,但在所有其他级别都具有相同的逻辑。

Sync/async代码具有根本不同的运行时期望。假设回答这个问题有点太宽泛了;一个具体的例子可能更能说明问题。deceze:下面@sraw给出的答案显示了一个例子,或者查看上面我的伪代码谢谢。这是有道理的。但请注意,您的两个
ls
函数几乎是重复的代码,因此应该避免使用。@JoshuaFox同意,实际上,如果我不使用
async/wait
关键字,而只使用旧的
yield
样式和
if
条件,就可以避免使用。但我们都知道,新的风格更清晰、更漂亮。此外,我相信没有办法完全避免重复的代码,我只是试图通过在最外部的级别上评估协同路由来复制最少的代码。
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)
# 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)