如何抑制显示后续异常的父异常(原因)

How to suppress displaying the parent exception (the cause) for subsequent exceptions

我知道 raise ... from None 并已阅读 How can I more easily suppress previous exceptions when I raise my own exception in response?

但是,如何在不控制从 except 子句执行的代码的情况下实现相同的效果(抑制 "During handling of the above exception, another exception occurred" 消息)?我认为 sys.exc_clear() 可以用于此,但 Python 中不存在该功能 3.

我为什么要问这个?我有一些简单的缓存代码,看起来像(简化):

try:
    value = cache_dict[key]
except KeyError:
    value = some_api.get_the_value_via_web_service_call(key)
    cache_dict[key] = value

当 API 调用出现异常时,输出将是这样的:

Traceback (most recent call last):
  File ..., line ..., in ...
KeyError: '...'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File ..., line ..., in ...
some_api.TheInterestingException: ...

这是一种误导,因为原来的 KeyError 根本不是真正的错误。我当然可以通过将 try/except (EAFP) 更改为密钥存在测试 (LBYL) 来避免这种情况,但这不是很 Pythonic 并且线程友好性较低(不是上面的线程-安全,但这不是重点)。

期望 some_api 中的所有代码将它们的 raise X 更改为 raise X from None 是不合理的(而且它在所有情况下都没有意义)。是否有一个干净的解决方案来避免错误消息中不需要的异常链?

(顺便说一句,奖金问题:我在示例中使用的缓存东西基本上等同于 cache_dict.setdefault(key, some_api.get_the_value_via_web_service_call(key)),如果只有 setdefault 的第二个参数可以是可调用的,并且只有在值需要设置。没有更好/规范的方法吗?)

您可以尝试自己抑制上下文:

try:
    value = cache_dict[key]
except KeyError:
    try:
        value = some_api.get_the_value_via_web_service_call(key)
    except Exception as e:
        e.__context__ = None
        raise

    cache_dict[key] = value

这里有几个选项。

首先,orlp 建议的更清晰的版本:

try:
    value = cache_dict[key]
except KeyError:
    try:
        value = some_api.get_the_value(key)
    except Exception as e:
        raise e from None
    cache_dict[key] = value

对于第二个选项,我假设有一个 return value 隐藏在你没有显示的地方:

try:
    return cache_dict[key]
except KeyError:
    pass
value = cache_dict[key] = some_api.get_the_value(key)
return value

第三个选项,LBYL:

if key not in cache_dict:
    cache_dict[key] = some_api.get_the_value(key)
return cache_dict[key]

对于奖金问题,定义你自己的dict子类定义__missing__:

class MyCacheDict(dict):

    def __missing__(self, key):
        value = self[key] = some_api.get_the_value(key)
        return value

希望对您有所帮助!

这是@Zachary 的第二个选项的一个版本,它的使用稍微简单一些。首先,dict 的一个辅助子类,它 returns 一个“未命中”的标志值而不是抛出异常:

class DictCache(dict):
    def __missing__(self, key):
        return self

当时正在使用:

cache = DictCache()
...
value = cache[K]
if value is cache:
    value = cache[K] = some_expensive_call(K)

请注意使用“is”而不是“==”以确保不会与有效条目发生冲突。

如果赋值的是一个简单的变量(即“值”而不是另一个变量“x.value”的属性),你甚至可以只写两行:

if (value := cache[K]) is cache:
    value = cache[K] = some_expensive_call(K)