gpt4 book ai didi

python - 从函数中收集警告的大多数 pythonic 方式

转载 作者:太空宇宙 更新时间:2023-11-03 10:48:36 24 4
gpt4 key购买 nike

考虑一个非常简单的函数:

def generate_something(data):
if data is None:
raise Exception('No data!')

return MyObject(data)

它的输出基本上是我想要创建的对象的实例,或者如果函数无法创建对象则抛出一个异常。我们可以说输出是 binary 因为它要么成功(并返回一个对象)要么失败(并返回一个异常)。

处理第三种状态(即“成功但有一些警告”)的最 Pythonic 方式是什么?

def generate_something(data):
warnings = []

if data is None:
raise Exception("No data!")

if data.value_1 == 2:
warnings.append('Hmm, value_1 is 2')

if data.value_2 == 1:
warnings.append('Hmm, value_2 is 1')

return MyObject(data), warnings

返回元组是处理此问题的唯一方法,还是可以从函数内部广播或产生警告并从调用者那里捕获它们?

最佳答案

内置选项:warnings

Python 在 warnings 中实现了一个内置的警告机制。模块。问题在于 warnings 维护一个全局 warnings filter ,这可能会无意中导致您的函数抛出的警告被抑制。这是问题的演示:

import warnings

def my_func():
warnings.warn('warning!')

my_func() # prints "warning!"

warnings.simplefilter("ignore")
my_func() # prints nothing

如果你想使用 warnings 不管这个,你可以使用 warnings.catch_warnings(record=True)在列表中收集所有抛出的警告:

with warnings.catch_warnings(record=True) as warning_list:
warnings.warn('warning 3')

print(warning_list) # output: [<warnings.WarningMessage object at 0x7fd5f2f484e0>]

自制选项

出于上述原因,我建议改用您自己的警告机制。有多种方法可以实现这一点:

  • 只返回警告列表

    开销最少的最简单的解决方案:只返回警告。

    def example_func():
    warnings = []

    if ...:
    warnings.append('warning!')

    return result, warnings

    result, warnings = example_func()
    for warning in warnings:
    ... # handle warnings
  • 将警告处理程序传递给函数

    如果您想在生成警告时立即处理它们,您可以重写您的函数以接受一个警告处理程序作为参数:

    def example_func(warning_handler=lambda w: None):
    if ...:
    warning_handler('warning!')

    return result


    def my_handler(w):
    print('warning', repr(w), 'was produced')

    result = example_func(my_handler)
  • contextvars ( python 3.7+)

    使用 python 3.7 我们得到了 contextvars模块,它使我们能够基于上下文管理器实现更高级别的警告机制:

    import contextlib
    import contextvars
    import warnings


    def default_handler(warning):
    warnings.warn(warning, stacklevel=3)

    _warning_handler = contextvars.ContextVar('warning_handler', default=default_handler)


    def warn(msg):
    _warning_handler.get()(msg)


    @contextlib.contextmanager
    def warning_handler(handler):
    token = _warning_handler.set(handler)
    yield
    _warning_handler.reset(token)

    使用示例:

    def my_warning_handler(w):
    print('warning', repr(w), 'was produced')

    with warning_handler(my_warning_handler):
    warn('some problem idk') # prints "warning 'some problem idk' was produced"

    warn(Warning('another problem')) # prints "Warning: another problem"

    注意事项:截至目前,contextvars 不支持生成器。 (相关 PEP 。)类似以下示例的内容将无法正常工作:

    def gen(x):
    with warning_handler(x):
    for _ in range(2):
    warn('warning!')
    yield

    g1 = gen(lambda w: print('handler 1'))
    g2 = gen(lambda w: print('handler 2'))

    next(g1) # prints "handler 1"
    next(g2) # prints "handler 2"
    next(g1) # prints "handler 2"
  • 没有 contextvars(适用于 python <3.7)

    如果您没有contextvars,您可以改用这个异步不安全的实现:

    import contextlib
    import threading
    import warnings


    def default_handler(warning):
    warnings.warn(warning, stacklevel=3)

    _local_storage = threading.local()
    _local_storage.warning_handler = default_handler


    def _get_handler():
    try:
    return _local_storage.warning_handler
    except AttributeError:
    return default_handler


    def warn(msg):
    handler = _get_handler()
    handler(msg)


    @contextlib.contextmanager
    def warning_handler(handler):
    previous_handler = _get_handler()
    _local_storage.warning_handler = handler

    yield

    _local_storage.warning_handler = previous_handler

关于python - 从函数中收集警告的大多数 pythonic 方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55917487/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com