为什么这个上下文管理器与字典理解的行为不同?

2024-04-23

我有一个上下文装饰器,完成后会产生副作用。我注意到,如果我使用字典理解,就不会出现副作用。

from contextlib import contextmanager
import traceback
import sys

accumulated = []

@contextmanager
def accumulate(s):
    try:
        yield
    finally:
        print("Appending %r to accumulated" % s)
        accumulated.append(s)

def iterate_and_accumulate(iterable):
    for item in iterable:
        with accumulate(item):
            yield item

def boom_unless_zero(i):
    if i > 0:
        raise RuntimeError("Boom!")

try:
    {i: boom_unless_zero(i) for i in iterate_and_accumulate([0, 1])}
except:
    traceback.print_exc()

print(accumulated)

print('\n=====\n')

try:
    {i: boom_unless_zero(i) for i in iterate_and_accumulate([0, 1])}
except:
    traceback.print_exc()

print(accumulated)
print('Finished!')

Output:

$ python2 boom3.py 
Appending 0 to accumulated
Traceback (most recent call last):
  File "boom3.py", line 25, in <module>
    {i: boom_unless_zero(i) for i in iterate_and_accumulate([0, 1])}
  File "boom3.py", line 25, in <dictcomp>
    {i: boom_unless_zero(i) for i in iterate_and_accumulate([0, 1])}
  File "boom3.py", line 22, in boom_unless_zero
    raise RuntimeError("Boom!")
RuntimeError: Boom!
[0]

=====

Appending 0 to accumulated
Appending 1 to accumulated
Traceback (most recent call last):
  File "boom3.py", line 34, in <module>
    {i: boom_unless_zero(i) for i in iterate_and_accumulate([0, 1])}
  File "boom3.py", line 34, in <dictcomp>
    {i: boom_unless_zero(i) for i in iterate_and_accumulate([0, 1])}
  File "boom3.py", line 22, in boom_unless_zero
    raise RuntimeError("Boom!")
RuntimeError: Boom!
[0, 0, 1]
Finished!
Appending 1 to accumulated

奇怪的是,副作用是在我的脚本“完成”后发生的。这意味着如果用户使用字典理解,则无法使用我的 contextdecorator。

我注意到这种行为在 Python 3 上消失了,如果我写的话这种行为也不会发生[boom_unless_zero(i) for i in iterate_and_accumulate([0, 1])]而不是字典理解。

为什么会出现这种情况?


From https://docs.python.org/2/reference/simple_stmts.html#the-yield-statement https://docs.python.org/2/reference/simple_stmts.html#the-yield-statement:

从 Python 版本 2.5 开始,yield 语句现在允许出现在 try ...finally 构造的 try 子句中。如果生成器在最终确定之前没有恢复(通过达到零引用计数或被垃圾收集),则将调用生成器迭代器的 close() 方法,从而允许执行任何挂起的 finally 子句。

换句话说,待生成器迭代器关闭之前,挂起的finally子句不会执行,无论是显式关闭还是由于垃圾收集(引用计数或循环)而关闭。看来 Python 2 列表推导式和 Python 3 在垃圾回收可迭代方面更有效。

如果您想明确关闭生成器迭代器:

from contextlib import closing

try:
    with closing(iter(iterate_and_accumulate(a))) as it:
        {i: boom_unless_zero(i) for i in it}
except:
    traceback.print_exc()
print(accumulated)

我研究了根本问题;看来问题在于生成器迭代器由异常回溯状态保持,因此另一个解决方法是调用sys.exc_clear():

import sys

try:
    {i: boom_unless_zero(i) for i in iterate_and_accumulate(a)}
except:
    traceback.print_exc()
    try:
        sys.exc_clear()
    except AttributeError:
        pass
print(accumulated)

在 Python 3 中,词法异常处理系统(http://bugs.python.org/issue3021 http://bugs.python.org/issue3021) 意味着异常状态在退出处理程序块时被清除,因此sys.exc_clear()没有必要(而且确实不存在)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么这个上下文管理器与字典理解的行为不同? 的相关文章

  • 如何使用 Python 和 Selenium WebDriver 获取 localStorage

    相当于什么 driver get cookies 获取 LocalStorage 而不是 ookies python API没有提供直接读 写本地存储的方法 但可以通过execute script class LocalStorage de
  • 出现异常时进行截图

    嘿 有没有一种方法可以在异常 任何异常 时捕获屏幕截图 我的 失败 解决方案位于BaseTestCase unittest TestCase子类 class BaseTestCase unittest TestCase classmetho
  • 函数不会抛出 bad_alloc 异常

    我正在尝试根据 Stroustrup 的 C PL4 书做一个练习 任务是 使用分配这么多内存new that bad alloc被抛出 报告如何 分配了多少内存以及花费了多少时间 执行此操作两次 一次不写入分配的内存 一次写入每个 元素
  • OpenPyXL - 如何查询单元格边框?

    python 和 openpyxl 都是新的 编写一个 py 脚本来遍历大量 Excel 工作簿 工作表 并且需要找到由边框格式标识的某些单元格 我在网上看到几个关于如何设置单元格边框的示例 但我需要阅读它们 具体来说 当表内的数据不一致但
  • 合并一个对(元组)列表?

    从链接对的列表中 我想将这些对组合成公共 ID 组 这样我就可以将 group ids 写回数据库 例如 UPDATE table SET group n WHERE id IN Example 1 2 3 4 1 5 6 3 7 8 be
  • time.sleep - TypeError:需要一个浮点[关闭]

    Closed 这个问题是无法重现或由拼写错误引起 help closed questions 目前不接受答案 time sleep 2 TypeError a float is required 我该如何解决 我不确定我应该在这里做什么 您
  • Python 中字典的合并层次结构

    我有两本词典 而我想做的事情有点奇怪 基本上 我想合并它们 这很简单 但它们是字典的层次结构 我想以这样的方式合并它们 如果字典中的项目本身就是字典并且存在于两者中 我也想合并这些字典 如果它不是字典 我希望第二个字典中的值覆盖第一个字典中
  • 如何获取一个类的所有实例

    我是一名初学者 正在学习 Python 我想创建一个课程Person 在构造函数中 我想将我创建的每个实例放入一个名为 实例 的集合中 然后我希望实例 方法返回所有实例 我怎样才能做到这一点 class Person Type annota
  • 在 Flask 中将配置文件作为字典读取

    在 instance app cfg 我已经配置 test test 在我的烧瓶文件 app py 中 with app open instance resource app cfg as f config f read print con
  • 计算两个节点之间的最长路径 NetworkX

    我正在尝试使用 Networkx 制作甘特图 网络中的所有节点都是完成项目所需执行的 任务 使用 Networkx 可以轻松计算项目的总时间 但是制作甘特图我需要每个节点的最新启动 NetworkX 包含一个函数 dag longest p
  • Python 请求包含有值的参数和没有值的参数

    我正在为 API 编写一个 Python 包装器 该 API 支持具有值的查询参数 例如param1如下 和查询参数do not有价值观 例如param2如下 即 https example com service param1 value
  • 多少次函数调用会导致堆栈溢出

    你好 Android Java 开发者 当一个函数调用一个函数并且该函数调用另一个函数等等时 有多少次调用 堆栈长度 会让我陷入堆栈溢出 有一般经验法则吗 我问的原因是因为我现在对于我的 5 人纸牌游戏来说哪个更有效 设计明智 解决方案一
  • 有没有比 ` except: pass` 更简洁的替代方案?

    我有一个函数 可以按偏好顺序返回多个组的随机成员 事情是这样的 def get random foo or bar I d rather have a foo than a bar if there are foos return get
  • Python - 根据条件调用函数

    我想知道是否有一种简洁的方法来根据条件调用函数 我有这个 if list 1 some dataframe df myfunction 我想知道这是否有可能三元运算符 http book pythontips com en latest t
  • 当输入是 DataFrame 时,在seaborn中对箱线图进行分组

    我打算在一个图中绘制多个列pandas dataframe 全部按另一列分组 使用groupby inside seaborn boxplot 对于类似的问题 这里有一个很好的答案matplotlib matplotlib 分组箱线图 ht
  • 在 Django 中删除特定用户的所有会话的最优化方法?

    我正在运行 Django 1 3 使用会话中间件和身份验证中间件 settings py SESSION ENGINE django contrib sessions backends db Persist sessions to DB S
  • Mxnet - 缓慢的数组复制到 GPU

    我的问题 我应该如何在 mxnet 中执行快速矩阵乘法 我的具体问题 数组复制到 GPU 的速度很慢 对此我们能做些什么呢 我创建随机数组 将它们复制到上下文中 然后相乘 import mxnet as mx import mxnet nd
  • 如何使用 NLP 确定句子中的中心词?

    例如 如果我得到一个句子 一名英国士兵在阿富汗战斗中丧生 这句话的中心词是 杀 给定 Python 中的 nltk 包 我怎样才能找到它 我不是在谈论词干 我指的是中心词 您正在寻找中心词句子解析 它可以在 Python 的 nltk 包中
  • Pandas 数据透视表同时包含多列

    我怀疑是否pandas pivot table可以一次接受两列并单独处理它们 而不是分层处理 假设我有以下数据框 id date day val 101 11 1 1 1 2 1 101 11 1 2 2 2 2 101 11 1 3 3
  • 检查一个数是否是完全平方数

    如何检查一个数是否是完全平方数 速度并不重要 目前 只是工作 See also Integer square root in python https stackoverflow com questions 15390807 依赖任何浮点计

随机推荐