3

この質問は、ジェネレーター内のエラーをキャッチしてから続き、その後続行します

Web サイトから URL などを抽出しようとする約 50 の同様の (ただし異なる) 関数があります。Web サイトはそれぞれ異なり、それぞれの機能も異なります。また、Web サイトは時間の経過とともに変化する傾向があるため、このコードは乱雑で信頼できません。

これは単純化されたサンプルです。または、最初の質問のサンプルを見てください。

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 の関数全体にブロックが散在するのは望ましくありません。これは可能ですか?もしそうなら、どうすれば最もDRYな方法でそれを行うことができますか?また、エラー処理を1か所で行うことはできますか?

更新:この質問には、以前continueはログと共にステートメントが含まれていましたが、mgilson が指摘したように、これは必要ありません。

UPDATE 2のgeorgeslの回答により、関数は次のようになります。

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これは、上記のステートメントが不要であることを前提としていることに注意してください。あまり慣れていないようですが、何かが足りないのかもしれません。

関数が実際に何かを返さない場合は、例外にヒットしたかどうかに応じてTrueorを返すことができます。それを使用してロジックFalseを記述できます。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 に答える