简体   繁体   English

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

[英]Python alternative assignment when exceptions occur using context manager

In Python, I can assign alternative values to a variable if the first assignment to this variable raise exceptions, something like: 在Python中,如果对该变量的第一次分配引发异常,则可以为该变量分配替代值,例如:

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

I wonder can we replace the try/except with context manager? 我想知道我们可以用上下文管理器替换try/except吗?

Here what I tried: 这是我尝试的:

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

I guess I must do something instead of yield but don't know what must I do. 我想我必须做些事情而不是yield但不知道该怎么办。 Any suggestion? 有什么建议吗?

The exception ( ZeroDivisionError in this case) is not caused by assignment failure, but becasue of dividing by 0 . 异常(在这种情况下为ZeroDivisionError )不是由分配失败引起的,而是由于被0

The first code can be converted as follow: 第一个代码可以如下转换:

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

How about the following approach (yielding default value, change the value in with statement body)? 如何使用以下方法(生成默认值, 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

There is no way for the context manager to know what you are doing within the context. 上下文管理器无法知道您在上下文中正在做什么。 It especially won't be able to tell which variable you are assigning a value to; 尤其是,它无法分辨出您要向哪个变量赋值; it also won't be able to access that variable; 它也将无法访问该变量; and even if it could, there would be no guarantee that you only did that one assigment inside the context manager too. 即使可以,也无法保证您也只能在上下文管理器中进行一次分配。 So, no, you can't do it like that. 所以,不,你不能那样做。

What you could do however, is to do it the other way around. 但是,您可以做的是另一种方法。 Your 0 is a default value, so you would set that one first . 您的0是默认值,因此您应将其设置为first Afterwards, you try to assign your actual value 1/0 and ignore the ZeroDivisionError. 之后,您尝试分配实际值1/0并忽略ZeroDivisionError。 So it would look like this: 所以它看起来像这样:

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

And that you can do with a context manager, with contextlib.suppress : 而且您可以使用contextlib.suppress使用上下文管理器进行操作:

a = 0
with suppress(ZeroDivisionError):
    a = 1/0

you can use Decorator like: 您可以像这样使用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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM