
[英]pyautogui does not work with python3.10, but works well with python3.8 on ubuntu20.04
[英]asyncio: works in Python 3.10 but not in Python 3.8
考虑以下代码:
import asyncio
sem: asyncio.Semaphore = asyncio.Semaphore(2)
async def async_run() -> None:
async def async_task() -> None:
async with sem:
await asyncio.sleep(1)
print('spam')
await asyncio.gather(*[async_task() for _ in range(3)])
asyncio.run(async_run())
使用 Python 3.10.6 (Fedora 35) 运行,它的工作原理与教科书中的一样。
但是,当我使用 Python 3.8.10 (Ubuntu 20.04) 运行它时,出现以下错误:
Traceback (most recent call last):
File "main.py", line 21, in <module>
asyncio.run(async_run())
File "/usr/lib/python3.8/asyncio/runners.py", line 44, in run
return loop.run_until_complete(main)
File "/usr/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete
return future.result()
File "main.py", line 18, in async_run
print(future_entry_index, await future_entry)
File "/usr/lib/python3.8/asyncio/tasks.py", line 619, in _wait_for_one
return f.result() # May raise f.exception().
File "main.py", line 11, in async_task
async with sem:
File "/usr/lib/python3.8/asyncio/locks.py", line 97, in __aenter__
await self.acquire()
File "/usr/lib/python3.8/asyncio/locks.py", line 496, in acquire
await fut
RuntimeError: Task <Task pending name='Task-4' coro=<async_run.<locals>.async_task() running at main.py:11> cb=[as_completed.<locals>._on_completion() at /usr/lib/python3.8/asyncio/tasks.py:606]> got Future <Future pending> attached to a different loop
它async with sem
线和导致错误的Semaphore
object 异步。 没有它,一切都没有错误,但不是我想要的方式。
我无法在任何地方提供loop
参数,即使在允许的地方,它自 Python 3.8 以来已被弃用,并在 Python 3.10 中被删除。
如何使代码与 Python 3.8 一起使用?
更新。 对asyncio
代码的一瞥表明 Python 版本差异很大。 但是,信号量不能在 3.8 中被打破,对吧?
正如在这个答案中所讨论的,pre-python 3.10 Semaphore 根据当前运行的循环在__init__
上设置它的循环,而asyncio.run
开始一个新的循环。 因此,当您尝试async.run
您的 coros 时,您使用的循环与定义Semaphore
的循环不同,正确的错误消息实际上是got Future <Future pending> attached to a different loop
。
幸运的是,让代码在两个 python 版本上工作并不难:
解决方案 1
不要创建新循环,使用现有循环运行您的 function:
import asyncio
sem: asyncio.Semaphore = asyncio.Semaphore(value=2)
async def async_task() -> None:
async with sem:
await asyncio.sleep(1)
print(f"spam {sem._value}")
async def async_run() -> None:
await asyncio.gather(*[async_task() for _ in range(3)])
loop = asyncio.get_event_loop()
loop.run_until_complete(async_run())
loop.close()
解决方案 2
在 asyncio.run 创建的循环中初始化信号量asyncio.run
:
import asyncio
async def async_task2(sem) -> None:
async with sem:
await asyncio.sleep(1)
print(f"spam {sem._value}")
async def async_run2() -> None:
sem = asyncio.Semaphore(2)
await asyncio.gather(*[async_task2(sem) for _ in range(3)])
asyncio.run(async_run2())
这两个片段都适用于 python3.8 和 python3.10。 大概是因为这样的怪异,他们从 python 3.10 中的大多数 asyncio 中删除了循环参数。
比较 3.8 和 3.10 的信号量的__init__
:
class Semaphore(_ContextManagerMixin):
"""A Semaphore implementation.
A semaphore manages an internal counter which is decremented by each
acquire() call and incremented by each release() call. The counter
can never go below zero; when acquire() finds that it is zero, it blocks,
waiting until some other thread calls release().
Semaphores also support the context management protocol.
The optional argument gives the initial value for the internal
counter; it defaults to 1. If the value given is less than 0,
ValueError is raised.
"""
def __init__(self, value=1, *, loop=None):
if value < 0:
raise ValueError("Semaphore initial value must be >= 0")
self._value = value
self._waiters = collections.deque()
if loop is None:
self._loop = events.get_event_loop()
else:
self._loop = loop
warnings.warn("The loop argument is deprecated since Python 3.8, "
"and scheduled for removal in Python 3.10.",
DeprecationWarning, stacklevel=2)
class Semaphore(_ContextManagerMixin, mixins._LoopBoundMixin):
"""A Semaphore implementation.
A semaphore manages an internal counter which is decremented by each
acquire() call and incremented by each release() call. The counter
can never go below zero; when acquire() finds that it is zero, it blocks,
waiting until some other thread calls release().
Semaphores also support the context management protocol.
The optional argument gives the initial value for the internal
counter; it defaults to 1. If the value given is less than 0,
ValueError is raised.
"""
def __init__(self, value=1, *, loop=mixins._marker):
super().__init__(loop=loop)
if value < 0:
raise ValueError("Semaphore initial value must be >= 0")
self._value = value
self._waiters = collections.deque()
self._wakeup_scheduled = False
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.