当前位置: 首页 > 知识库问答 >
问题:

为什么_uaexit_u_u_______________________________?

百里鸿祯
2023-03-14

这是我代码的简化版本:

main是在第二次迭代后停止的协程。
get_numbers是一个异步生成器,它在异步上下文管理器中生成数字。

import asyncio


class MyContextManager:
    async def __aenter__(self):
        print("Enter to the Context Manager...")
        return self

    async def __aexit__(self, exc_type, exc_value, exc_tb):
        print(exc_type)
        print("Exit from the Context Manager...")
        await asyncio.sleep(1)
        print("This line is not executed")  # <-------------------
        await asyncio.sleep(1)


async def get_numbers():
    async with MyContextManager():
        for i in range(30):
            yield i


async def main():
    async for i in get_numbers():
        print(i)
        if i == 1:
            break


asyncio.run(main())

输出为:

Enter to the Context Manager...
0
1
<class 'asyncio.exceptions.CancelledError'>
Exit from the Context Manager...

其实我有两个问题:

  1. 据我所知,AsyncIO将在事件循环的下一个周期中很快调用一个任务,并为执行提供了机会。但打印行(“该行未执行”)未执行。为什么?假设如果我们在\uuuaexit\uuuu0>中有一个await语句,那么该行后面的代码根本不会执行,我们不应该依赖它来清理,这是正确的吗
 |  aclose(...)
 |      aclose() -> raise GeneratorExit inside generator.

那么为什么我得到

*我正在使用Python 3.10.4


共有3个答案

颛孙沈义
2023-03-14
匿名用户

回答第一个问题:

如果我们在\uuaexit\uuuuu>中有一个await语句,那么该行后面的代码根本不会执行,这是正确的吗?

我会说不,情况并非总是如此。只要main有足够的时间并且可以再次将控制传递回事件循环,__aexit__中的代码就可以执行。我尝试了这个:

async def main():
    async for i in get_numbers():
        print(i)
        if i == 1:
            break
    await asyncio.sleep(4)   #  <---- New

<代码>。run()只关心传递给它并最终运行的协同路由,而不关心其他协同路由,包括\uu aexit\uu。因此,如果它没有足够的时间或没有将控制传递给事件循环,我就不能依赖于第一个wait语句之后的下一行。

可能有助于的其他信息:

这里是base_events。py/run_forever方法(由.run()调用),我发现了自己_asyncgen\u finalizer\u钩子传递给系统。设置异步钩子。asyncgen\u finalizer\u钩子的主体是:

    def _asyncgen_finalizer_hook(self, agen):
        self._asyncgens.discard(agen)
        if not self.is_closed():
            self.call_soon_threadsafe(self.create_task, agen.aclose())

但是call_soon_threadsafe的实现是空的。

我稍后会清理这个答案并消除这些猜测。

杨豪
2023-03-14

答案很简单:解释器会在一秒钟后继续执行__aexit__,但是main函数完成了,没有指向上下文管理器的指针。

你提到的第一个明显的解决方案是在main函数之后等待足够长的时间:

async def main():
    async for i in get_numbers():
        print(i)
        if i == 1:
            break
    await asyncio.sleep(4)   #  <---- New

另一种方法是使用try/finally:

    async def __aexit__(self, exc_type, exc_value, exc_tb):
        try:
            pass
            print(exc_type)
            print("Exit from the Context Manager...")
            await asyncio.sleep(1)
        finally:
            print("This line is not executed")  # <-------------------
宗政英才
2023-03-14

我不确定发生了什么,但发布我的发现,以防它被证明对决定调查的其他人有用。当我们将对get_numbers()的引用存储在main()之外时,输出会更改为预期。我会说get_numbers()似乎是垃圾收集到早期,但是禁用gc没有帮助,所以我的猜测可能是错误的。

import asyncio


test = None


class MyContextManager:
    async def __aenter__(self):
        print("Enter to the Context Manager...")
        return self

    async def __aexit__(self, exc_type, exc_value, exc_tb):
        print(exc_type)
        print("Exit from the Context Manager...")
        await asyncio.sleep(1)
        print("This line is not executed")  # <-- Executed now
        await asyncio.sleep(1)


async def get_numbers():
    async with MyContextManager():
        for i in range(30):
            yield i


async def main():
    global test
    test = get_numbers()

    async for i in test:
        print(i)
        if i == 1:
            break


asyncio.run(main())
 类似资料:
  • 问题内容: 在Java 8中,我们有类,它奇怪地有一个方法 因此,您可能希望它实现接口,而该接口正是需要此方法,但事实并非如此。 当我想使用foreach循环遍历Stream时,我必须做类似的事情 我在这里想念什么吗? 问题答案: 人们已经在邮件列表 asked 上问过同样的问题。主要原因是Iterable也具有可重复的语义,而Stream没有。 我认为主要原因是Iterable暗示可重用性,而S

  • 问题内容: 我是一名即将毕业的计算机科学专业的学生,​​在我的整个编码生涯中,我发现很少使用枚举的实例,除了典型的情况(例如代表标准纸牌的面孔)外,还使用了枚举。 您是否知道在日常编码中使用枚举的任何巧妙方法? 为什么枚举如此重要,在什么情况下应该能够确定建立枚举是最佳方法? 问题答案: 这些是主要的论点,以及短的例子。 的情况 从Java 6开始,是一个凌乱类的示例,该类可以从使用中受益匪浅(除

  • Bootstrapping(引导) 是 Netty 中配置程序的过程,当你需要连接客户端或服务器绑定指定端口时需要使用 Bootstrapping。 如前面所述,Bootstrapping 有两种类型,一种是用于客户端的Bootstrap,一种是用于服务端的ServerBootstrap。不管程序使用哪种协议,无论是创建一个客户端还是服务器都需要使用“引导”。 面向连接 vs. 无连接 请记住,这

  • 问题内容: 在我的Web应用程序中,我在Apache Tomcat(TomEE)/7.0.37服务器上使用OpenJPA。我使用Netbeans自动生成类(“来自数据库的实体类…”和“来自实体类的会话Bean …”)。在SessionBean(例如UserFacade)上,我想获取EntityManager: 但是当我通过上述方式得到它时,我得到的是空值。当我通过: ecm不为空,还可以 我的pe

  • 问题内容: 我有Ubuntu 10.10和apache2,php 5.3.3-1和mysql 5.1。 我正在通过URL向页面传递一些值。在该页面上,如果我这样做了,那么我会看到数组的内容。但是,如果我这样做数组是空的。任何想法为什么会这样? 问题答案: 也可以尝试检查php.ini中的“ request_order” 选项:

  • 我的问题是无法使用和获取文本字段。我尝试使用XPath来选择对象,但没有成功。 这是我的代码: 我重新提出这个问题是因为旧的问题有点让人困惑,我想。这是旧的 XML形式的我的文档(Document.getXML()) 我需要选择文本字段,做一个邮件合并,我的计划将是复制和移动字段。如果有更好的方法,我愿意尝试一下:)