简体   繁体   中英

How to decorate iterables with an error handler?

Suppose we have two kinds of methods: one returns a list, the other returns an iterator. So they are very comparable in the sense that both return values are iterable.

I'd like to write a decorator that catches errors inside the iteration. The problem is that the iterator is returned without iteration and so no errors will be caught.

In the below code, the wrapped_properly decorator works around the issue by providing two separate wrappers, a default one ( wrapper ) and one specifically for generator functions ( generatorfunctionwrapper ). The approach feels quite complicated and verbose.

from inspect import isgeneratorfunction
from functools import wraps

def failing_generator():
    for i in range(1, 5):
        if i % 2 == 0:
            print('I dont like even numbers.')
            raise ValueError(i)
        yield i

def wrapped_badly(fn):
    @wraps(fn)
    def wrapper(*args, **kwargs):
        try:
            return fn(*args, **kwargs)
        except ValueError as err:
            print('Not to worry.')
    return wrapper

def wrapped_properly(fn):
    @wraps(fn)
    def wrapper(*args, **kwargs):
        try:
            return fn(*args, **kwargs)
        except ValueError as err:
            print('Not to worry.')
    
    @wraps(fn)
    def generatorfunctionwrapper(*args, **kwargs):
        try:
            yield from fn(*args, **kwargs)
        except ValueError as err:
            print('Not to worry.')

    if isgeneratorfunction(fn):
        return generatorfunctionwrapper
    else:
        return wrapper

for x in wrapped_properly(failing_generator)():
    print(x)
# Prints:
# 1
# I dont like even numbers.
# Not to worry.

for x in wrapped_badly(failing_generator)():
    print(x)
# Prints:
# 1
# I dont like even numbers.
# Traceback (most recent call last):
# ...
# ValueError: 2

Is there a better/more pythonic way to do this?

I would suggest returning an iterator no matter what iterable the original function returns.

def wrapped(fn):
    def wrapper(*args, **kwargs):
        try:
            yield from iter(fn(*args, **kwargs))
        except ValueError as err:
            print('Not to worry')

    return wrapper

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