1

再帰関数のためにデコレータをトレースしました。tryブロックに戻る方法が知りたいです。while ループを試してみましたが、関数が再帰的であるためうまくいきません。問題は、関数 change_t が例外を発生させたときです。try ブロックの実行を続行したい

ここに私のデコレータと機能があります

適切な結果:

,- change_t([9, 7, 5], 44)
| ,- change_t([9, 7, 5], 35)
| | ,- change_t([9, 7, 5], 26)
| | | ,- change_t([9, 7, 5], 17)
| | | | ,- change_t([9, 7, 5], 8)
| | | | | ,- change_t([7, 5], 8)
| | | | | | ,- change_t([7, 5], 1)
| | | | | | | ,- change_t([5], 1)
| | | | | | | | ,- change_t([], 1)
| | | | | | ,- change_t([5], 8)
| | | | | | | ,- change_t([5], 3)
| | | | | | | | ,- change_t([], 3)
| | | | | | | ,- change_t([], 8)
| | | | ,- change_t([7, 5], 17)
| | | | | ,- change_t([7, 5], 10)
| | | | | | ,- change_t([7, 5], 3)
| | | | | | | ,- change_t([5], 3)
| | | | | | | | ,- change_t([], 3)
| | | | | | ,- change_t([5], 10)
| | | | | | | ,- change_t([5], 5)
| | | | | | | | ,- change_t([5], 0)
| | | | | | | | `- []
| | | | | | | `- [5]
| | | | | | `- [5, 5]
| | | | | `- [5, 5]
| | | | `- [7, 5, 5]
| | | `- [7, 5, 5]
| | `- [9, 7, 5, 5]
| `- [9, 9, 7, 5, 5]
`- [9, 9, 9, 7, 5, 5]

これは私が得たものです:例外が発生するとすぐに停止します

change_t([9, 7, 5], 44)
,- change_t ([9, 7, 5], 44)
| ,- change_t ([9, 7, 5], 35)
| | ,- change_t ([9, 7, 5], 26)
| | | ,- change_t ([9, 7, 5], 17)
| | | | ,- change_t ([9, 7, 5], 8)
| | | | | ,- change_t ([7, 5], 8)
| | | | | | ,- change_t ([7, 5], 1)
| | | | | | | ,- change_t ([5], 1)
| | | | | | | | ,- change_t ([], 1)
| | | | | | `- 1
| | | | | `- 1
| | | | `- 1
| `- 8
`- 8
`- 17
`- 26
`- 35
`- 44
44
4

2 に答える 2

2

コメントで述べたように、元の関数が実際に例外をキャッチして続行できるように、例外を再発生させる必要があります。左に行きすぎないように、except ハンドラー内でインデントを 1 つだけ減らします。

class traced(object):
    indent =0
    def __init__(self,f):
        self.__name__=f.__name__
        self.indent=0
        self.f=f         
    def __call__(self,*args,**kwargs):
        string=""           
        if kwargs:
           l=[]
           for (key, value) in kwargs.items():
               l.append(str(key) + "=" + str(value))
           a=', '.join(l)
           string = '('+a+')'              
        else:
             l=[]
             for value in args:
                 l.append(str(value))
             a=', '.join(l)
             string = '('+a+')'       
        print('| ' * traced.indent + ',- '+ self.__name__+' '+string)   
        try:
            traced.indent+=1
            value = self.f(*args,**kwargs)                
        except Exception:
            traced.indent-=1  # <-- only decrement by one
            raise             # <-- reraise the exception so the original function can catch it
        traced.indent-=1
        print('| '* traced.indent + "`- "+ repr(value))          
        return value

そして、それは動作します:

>>> change_t([9, 7, 5], 44)
,- change_t ([9, 7, 5], 44)
| ,- change_t ([9, 7, 5], 35)
| | ,- change_t ([9, 7, 5], 26)
| | | ,- change_t ([9, 7, 5], 17)
| | | | ,- change_t ([9, 7, 5], 8)
| | | | | ,- change_t ([7, 5], 8)
| | | | | | ,- change_t ([7, 5], 1)
| | | | | | | ,- change_t ([5], 1)
| | | | | | | | ,- change_t ([], 1)
| | | | | | ,- change_t ([5], 8)
| | | | | | | ,- change_t ([5], 3)
| | | | | | | | ,- change_t ([], 3)
| | | | | | | ,- change_t ([], 8)
| | | | ,- change_t ([7, 5], 17)
| | | | | ,- change_t ([7, 5], 10)
| | | | | | ,- change_t ([7, 5], 3)
| | | | | | | ,- change_t ([5], 3)
| | | | | | | | ,- change_t ([], 3)
| | | | | | ,- change_t ([5], 10)
| | | | | | | ,- change_t ([5], 5)
| | | | | | | | ,- change_t ([5], 0)
| | | | | | | | `- []
| | | | | | | `- [5]
| | | | | | `- [5, 5]
| | | | | `- [5, 5]
| | | | `- [7, 5, 5]
| | | `- [7, 5, 5]
| | `- [9, 7, 5, 5]
| `- [9, 9, 7, 5, 5]
`- [9, 9, 9, 7, 5, 5]
[9, 9, 9, 7, 5, 5]

最後に、デコレータを少しクリーンアップして、何をしているのかをより簡潔で明確にします。

class traced(object):
    indent = 0

    def __init__(self, f):
        self.__name__ = f.__name__
        self.f = f

    def __call__(self, *args, **kwargs):
        if kwargs:
            l = [str(key) + '=' + str(value) for key, value in kwargs.items()]
        else:
            l = list(map(str, args))
        print('| ' * traced.indent + ',- {0} ({1})'.format(self.__name__, ', '.join(l)))
        try:
            traced.indent += 1
            value = self.f(*args,**kwargs)                
        finally:
            traced.indent -= 1

        print('| ' * traced.indent + '`- ' + repr(value))
        return value

ここでは、リスト内包表記を使用するために引数の集約全体を単純化しました。また、文字列の書式設定を使用して、書式を少し明確にしました。そうすれば、リストの内容を囲む必要があった括弧を組み合わせることもできます (両方の場合で行いました)。また、実際に例外を確認せずに例外を再発生させる場合、最初から例外をキャッチする必要はありませんが、finally ブロックのインデントを調整するようにしてください。

実際、変数引数またはキーワード引数のいずれかをチェックする必要はありません。両方を受け入れるだけです:

l = list(map(str, args))
l.extend([str(key) + '=' + str(value) for key, value in kwargs.items()])
于 2013-03-07T12:04:40.030 に答える
0

例外が発生すると、発生元のコードから制御が解放されます。代わりに、その例外をキャッチする最初の catch ブロック、またはメインのインタープリター ループに渡されます。

あなたのコードを見てください:

if a==0:
    return []
elif len(l)==0:
    raise ChangeException()
elif l[0]>a:
    return change_t(l[1:],a)

3 番目の条件が真の場合、つまりl[0]>al の長さが 1 の場合はどうなるでしょうか。次に、次の呼び出しで、つまり、return change_t(l[1:],a)メインループ以外の誰にもキャッチされない例外が発生します。これが、コードが失敗する理由です。達成したい内容に応じて、3 番目の条件を try catch 句でラップする必要があります。

于 2013-03-07T12:04:17.603 に答える