Python 更改函数中的每个for循环,以便在每次失败的迭代后自动执行错误处理

Python 更改函数中的每个for循环,以便在每次失败的迭代后自动执行错误处理,python,for-loop,Python,For Loop,这个问题源自 我有大约50个类似(但不同)的函数,试图从网站中提取URL等。因为每个网站都不同,每个功能也不同,而且网站往往会随着时间的推移而改变,所以这些代码很混乱,不可信 这是一个简化的示例,或者看看第一个问题中的示例 def _get_units(self): for list1 in self.get_list1(): for list2 in self.get_list2(list1): for unit in list2:

这个问题源自

我有大约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)
对于我的函数中的每个

但我想用一种蟒蛇式的方式。我不想尝试:除了散落在我需要修改的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
这确实干净多了。不过,仅仅是一个装饰师就更好了。有人能告诉我这是否可行吗?如果没有,我将接受乔治的回答。

我可能会“装饰”函数本身。如果你生活在枯燥的原则中,你可能会把它们存储在一个列表或其他东西中:

def decorate_function(func):
    def decorated(x):
        try:
            return func(x)
        except Exception as e:
            log_error(e)
    return decorated
现在你可以用它来修饰你的函数,它会记录你的错误。注意,这假设上面的
continue
语句是不必要的。看起来我还不习惯,但我可能错过了什么

如果函数确实没有返回某些内容,那么您可以返回
True
False
,具体取决于您是否遇到异常。您可以使用它来编写
continue
逻辑。比如:

if not decorated_function(x): continue

您可能需要使用装饰器或更好的:


我对此进行了更多的思考,真正符合我需求的唯一解决方案似乎是修改代码本身。下面是:

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")

谢谢你帮我到达那里

您最内部的尝试是多余的--
yield
不会引发异常。在异常处理中
continue
的目的是什么?也许我遗漏了什么,但似乎没必要。(请注意,如果您可以删除
continue
,此问题会变得更严重。)easier@StevenRumbalski:由于第二个函数不是编写的,而是计算的,所以我让
try:except
,因为不管循环的内容如何,它都会在那里。@mgilson想到它……我怎么没有注意到?如果我
try:except
整个block,我不需要
继续
…编辑…是否可以将一些错误处理移到
get_list1
get_list2
?@StevenRumbalski--但这不是上面的代码snipet所能做的,是吗?每个
for
都包含一个
try except
。因此,如果你发现一个错误,你会点击
except>
然后是
continue
,它只是继续当前循环。这是我想要的,但不是现在的情况。请注意,第二个代码段是我想要第一个代码段的行为方式,它不是现有的代码。@ToonAlfrink--我理解。我看不到的是您的两个示例之间的关系。(这两个版本是带有
self…
的版本,在该版本中,与实际调用函数的版本相比,
产生一个值。)不是说我不认识谷歌,但是你能提供一个到
contextmanager
文档的链接吗?我也在考虑上下文管理器,但你还是每次都用…
重复
部分,而不是修饰使其自动运行的函数。你是对的@mgilson,也许我可以用g来节省一些空间Generator,但我没有成功地使其工作。这看起来不错,但很抱歉,我还不熟悉
with
语句。我已经尝试实现了这一点,但我没有真正理解它。请在我的函数中实现您的答案,以便我可以查看?和测试?
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")