简体   繁体   中英

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:

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

I wonder can we replace the try/except with context manager?

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. Any suggestion?

The exception ( ZeroDivisionError in this case) is not caused by assignment failure, but becasue of dividing by 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)?

>>> 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 . Afterwards, you try to assign your actual value 1/0 and ignore the 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 :

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

you can use Decorator like:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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