3

这个问题来自生成器中的捕获错误,然后继续

我有大约 50 个类似(但不同)的函数,它们试图从网站中提取 URL 等。因为每个网站都是不同的,每个功能都是不同的,而且因为网站往往会随着时间的推移而改变,所以这个代码是混乱的,不能被信任。

这是一个简化的示例,或者看第一个问题中的示例

def _get_units(self):
    for list1 in self.get_list1():
        for list2 in self.get_list2(list1):
            for unit in list2:
                yield unit

我想用这个函数做的基本上是改变行为以匹配这个:

def _get_units(self):
    for list1 in self.get_list1():
        try:                 
            for list2 in self.get_list2(list1):
                try:
                    for unit in list2:
                        try:
                            yield unit
                        except Exception as e:
                            log_exception(e)
                except Exception as e:
                    log_exception(e)
        except Exception as e:
            log_exception(e)

总之,我想把这个

for x in list:
    do_stuff(x)

对此:

for x in list:
    try:
        do_stuff(x)
    except Exception as e:
        log_exception(e)

对于for我的职能中的每一个。

但我想以pythonic的方式来做。我不希望try:except块分散在我需要更改的 50 个函数中。这可能吗?如果是这样,我怎样才能以最干燥的方式做到这一点,我可以在一个地方进行错误处理吗?

更新:这个问题以前包括一个continue与日志记录一起的语句,但正如 mgilson 指出的那样,这不是必需的。

使用georgesl的答案更新2,函数如下:

from contextlib import contextmanager

@contextmanager
def ErrorManaged():
    try:
        yield
    except Exception as e:
        log_exception(e)


def _get_units(self):
    for list1 in self.get_list1():
        with ErrorManaged():              
            for list2 in self.get_list2(list1):
                with ErrorManaged():
                    for unit in list2:
                        with ErrorManaged():
                            yield unit

这确实更清洁。不过,单纯的装饰者会更好。谁能告诉我这是否可能?如果没有,我会接受georgesl的回答。

4

3 回答 3

3

您可能想要使用装饰器或更好的上下文管理器

from contextlib import contextmanager


def HandleError(func):

    def wrapped(*args, **kwargs):

        try:
            func(*args, **kwargs)
        except Exception:
            print "Bug on node #", args[0]


    return wrapped

@contextmanager
def ErrorManaged():
    try:
        yield
    except Exception:
        print "Oh noes, the loop crashed"



@HandleError
def do_something(x):
    print x
    if x==5:
        raise('Boom !')




with ErrorManaged():
    for x in range(10):
        do_something(x)
        if x == 7 :
            raise('aaaah !')
于 2012-11-30T16:06:46.380 回答
2

我可能会“装饰”这些功能本身。如果您遵循 DRY 原则,大概会将它们存储在列表或其他东西中:

def decorate_function(func):
    def decorated(x):
        try:
            return func(x)
        except Exception as e:
            log_error(e)
    return decorated

现在你可以用它来装饰你的函数,它会记录你的错误。请注意,这假定上述continue声明是不必要的。看起来我真的不习惯,但我可能会遗漏一些东西。

如果函数确实没有返回某些内容,那么您可以返回TrueFalse取决于您是否遇到异常。你可以用它来编写你的continue逻辑。就像是:

if not decorated_function(x): continue
于 2012-11-30T15:47:32.470 回答
0

我对此进行了更多思考,真正满足我需求的唯一解决方案似乎是修改代码本身。所以这里是:

from contextlib import contextmanager
import inspect

@contextmanager
def ErrorManaged():
    try:
        yield
    except Exception as e:
        print e



def get_units():
    for x in range(-5,5):
        print(x)

        if x % 3 == 0:
            raise Exception("x nope")

        for y in range(-5,5):
            print("\t{}".format(y))

            if y % 3 == 0:
            raise Exception("y nope")

            for z in range(-5,5):
                print("\t\t{}".format(z))

                if z % 3 == 0:
                    raise Exception("z nope")


import re

def modify_get_units(get_units):    
    lines = inspect.getsourcelines(get_units)[0]
    add = "with ErrorManaged():\n"
    new = []
    tabsize = 0
    for c in lines[1]:
        if c == " ":
            tabsize += 1
        else:
            break

    count = 0
    for line in lines:
        new.append(" " * tabsize * count + line)
        m = re.match(r"^(\s+)for\s[()\w,]+\sin\s[^ :\n]+:\n$",line)
        if m:
            count += 1
            new.append(m.group(1) + " " * tabsize * count + add)

    return "".join(new)

oldfunc = inspect.getsource(get_units)
newfunc = modify_get_units(get_units)

#printing function bodies to show results

print(oldfunc)
print("\n\n\n")
print(newfunc)


#re-declare get_units
exec newfunc

#execute, but now now
#get_units()

输出:

toon@ToonAlfrinkPC ~ $ python test.py
def get_units():
    for x in range(-5,5):
        print(x)

        if x % 3 == 0:
            raise Exception("x nope")

        for y in range(-5,5):
            print("\t{}".format(y))

            if y % 3 == 0:
                raise Exception("y nope")

            for z in range(-5,5):
                print("\t\t{}".format(z))

                if z % 3 == 0:
                    raise Exception("z nope")





def get_units():
    for x in range(-5,5):
        with ErrorManaged():
            print(x)

            if x % 3 == 0:
                raise Exception("x nope")

            for y in range(-5,5):
                with ErrorManaged():
                    print("\t{}".format(y))

                    if y % 3 == 0:
                        raise Exception("y nope")

                    for z in range(-5,5):
                        with ErrorManaged():
                            print("\t\t{}".format(z))

                            if z % 3 == 0:
                                raise Exception("z nope")

感谢您帮助我到达那里!

于 2012-12-01T01:19:52.513 回答