从函数中收集警告的最具python风格的方法
考虑一个非常简单的函数:从函数中收集警告的最具python风格的方法,python,python-3.x,Python,Python 3.x,考虑一个非常简单的函数: def generate_something(data): if data is None: raise Exception('No data!') return MyObject(data) 它的输出基本上是我想要创建的对象的实例,或者如果函数无法创建对象,则是异常。我们可以说输出是二进制的,因为它要么成功(并返回一个对象),要么不成功(并返回一个异常) 处理第三种状态,即“成功但有一些警告”的最具python风格的方式是什么 返回
def generate_something(data):
if data is None:
raise Exception('No data!')
return MyObject(data)
它的输出基本上是我想要创建的对象的实例,或者如果函数无法创建对象,则是异常。我们可以说输出是二进制的,因为它要么成功(并返回一个对象),要么不成功(并返回一个异常)
处理第三种状态,即“成功但有一些警告”的最具python风格的方式是什么
返回元组是处理此问题的唯一方法,还是可以从函数中广播或产生警告,并从调用方捕获它们?您可以返回异常列表(或自定义异常),其中有问题供以后处理:
class MyWarning(Warning):
pass
def generate_something(data):
warnings = []
if data is None:
raise Exception("No data!")
if data.value_1 == 2:
warnings.append(MyWarnin('Hmm, value_1 is 2'))
if data.value_2 == 1:
warnings.append(MyWarning('Hmm, value_2 is 1'))
return MyObject(data), warnings
然后举例来说:
def handle_warnings(warnings):
for w in warnings:
try:
raise w
except MyWarning:
...
except AttributeError: #in case you want to handle other type of errors
...
内置选项:
Python在模块中实现了内置的警告机制。问题是,警告
维护一个全局值,这可能会无意中导致函数抛出的警告被抑制。下面是问题的演示:
import warnings
def my_func():
warnings.warn('warning!')
my_func() # prints "warning!"
warnings.simplefilter("ignore")
my_func() # prints nothing
如果要使用警告
而不考虑这一点,则可以使用收集列表中所有抛出的警告:
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(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)
- (python 3.7+)
在python 3.7中,我们得到了一个模块,它允许我们基于上下文管理器实现更高级别的警告机制:
用法示例: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
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"
警告将更有意义
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"
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"
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