使用上下文管理器发生异常时的Python替代分配

时间:2014-12-09 08:40:52

标签: python contextmanager

在Python中,如果对此变量的第一次赋值引发异常,我可以为变量分配替代值,如:

try:
    a = 1/0
except Exception:
    a = 0

我想知道我们可以用上下文管理器替换try/except吗?

这是我试过的:

from contextlib import contextmanager

@contextmanager
def tryo(*exception_list):
    t = tuple(i for i in exception_list[0]
              if isinstance(i,Exception) or (Exception,))
    try:
        yield
    except t as e:
        print e

with tryo([(Exception, 0)]):
    a = 1/0

我想我必须做一些事情,而不是yield,但不知道我该做什么。有什么建议吗?

3 个答案:

答案 0 :(得分:2)

异常(在这种情况下为ZeroDivisionError)不是由分配失败引起的,而是由0除以。

第一个代码可以转换如下:

a = 0
try:
    a = 1 / 0
except Exception:  # ZeroDivisionError:
    pass

以下方法如何(产生默认值,更改with语句体中的值)?

>>> from contextlib import contextmanager
>>>
>>> @contextmanager
... def tryo(exceptions, default):
...     try:
...         yield default
...     except exceptions:
...         pass
...
>>> with tryo((Exception), 0) as a:  # ZeroDivisionError:
...     a = 1 / 0
...
>>> with tryo((Exception), 0) as b:  # ZeroDivisionError:
...     b = 4 / 2
...
>>> a
0
>>> b
2.0

答案 1 :(得分:2)

上下文管理器无法知道您在上下文中执行的操作。它尤其无法分辨您为哪个变量赋值;它也将无法访问该变量;即使可能,也不能保证你只在上下文管理器中做了一个分配。所以,不,你不能那样做。

然而,你可以做的是反过来做。您的0是默认值,因此您可以将设置为。然后,您尝试分配实际值1/0并忽略ZeroDivisionError。所以它看起来像这样:

a = 0
try:
    a = 1/0
except ZeroDivisionError:
    pass

您可以使用contextlib.suppress

来处理上下文管理器
a = 0
with suppress(ZeroDivisionError):
    a = 1/0

答案 2 :(得分:0)

你可以像以下一样使用Decorator:

def my_decorator(exception_list):
    def real_decorator(func):
        def fn_wrapper(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except tuple(e for e, _ in exception_list) as e:
                for error, default in exception_list:
                    if isinstance(e, error):
                        return default
                else:
                    # this Exception not in exception_list
                    raise e
        return fn_wrapper
    return real_decorator


@my_decorator([(ZeroDivisionError, 1),
               (IndexError, 2),
               (ValueError, 3),
               (Exception, 0)],
              )
def div_working():
    import random
    e = random.choice((ZeroDivisionError, IndexError, ValueError, Exception, 100, 200, 300))
    if isinstance(e, int):
        return e
    else:
        print e
        raise e

for _ in range(10):
    a = div_working()
    print a
    print "= " * 10