10

私はプログラミング初心者で、Python の教科書 (Magnus Lie Hetland による「Beginning Python」) の例を理解するのに苦労しています。この例は、ネストされたリストの要素を (任意の深さで) フラット化するように設計された再帰ジェネレーターです。

def flatten(nested):
    try:
        for sublist in nested:
            for element in flatten(sublist):
                yield element
    except TypeError:
        yield nested

次に、ネストされたリストを次のようにフィードします。

>>> list(flatten([[[1],2],3,4,[5,[6,7]],8]))
[1,2,3,4,5,6,7,8]

flatten() 内の再帰が、このリストの最も内側の要素である '1' に絞り込むのにどのように役立つかは理解していますが、'1' が実際に flatten() に 'nested '。これは TypeError (数値を反復処理できない) につながり、出力を生成するために実際に重労働を行うのは例外処理であると考えましたが、変更されたバージョンの flatten() でテストしたところ、確信が持てました。そうではないということです。代わりに、「yield element」行が原因のようです。

そうは言っても、私の質問はこれです...どうすれば「yield要素」を実際に実行できますか? 「ネストされた」はリストのようです-この場合、再帰の別のレイヤーが追加されます-またはそれは数値であり、TypeErrorが発生します。

これについての助けをいただければ幸いです...特に、 flatten() が次のような単純な例を処理するため、一連のイベントを順を追って説明していただければ幸いです。

list(flatten([[1,2],3]))
4

5 に答える 5

12

I have added some instrumentation to the function:

def flatten(nested, depth=0):
    try:
        print("{}Iterate on {}".format('  '*depth, nested))
        for sublist in nested:
            for element in flatten(sublist, depth+1):
                print("{}got back {}".format('  '*depth, element))
                yield element
    except TypeError:
        print('{}not iterable - return {}'.format('  '*depth, nested))
        yield nested

Now calling

list(flatten([[1,2],3]))

displays

Iterate on [[1, 2], 3]
  Iterate on [1, 2]
    Iterate on 1
    not iterable - return 1
  got back 1
got back 1
    Iterate on 2
    not iterable - return 2
  got back 2
got back 2
  Iterate on 3
  not iterable - return 3
got back 3
于 2012-07-07T17:47:06.747 に答える
6

おそらく、混乱の原因の 1 つは、最後のyieldステートメントをステートメントであるかのように考えていることですreturn。実際、このコードで aTypeErrorがスローされると、渡された項目が「返される」ことを示唆する人が何人かいます。そうではありません!

関数に出現するたびyieldに、結果は単一のアイテムではなく反復可能であることを覚えておいてください。したがって、 に渡す1flatten、結果は1 アイテムの generator になります。アイテムを取り出すには、それを反復処理する必要があります。

この 1 アイテム ジェネレーターは反復可能であるためTypeError、内側のforループがそれを反復しようとしても a をスローしません。ただし、内側のforループは 1 回だけ実行されます。次に、外側のforループは、ネストされたリスト内の次の iterable に移動します。

これについて考える別の方法は、反復不可能な値を に渡すたびにflatten、その値を 1 項目の反復可能オブジェクトにラップし、それを「返す」ということです。

于 2012-07-07T18:33:12.843 に答える
4

あなたが一般的に理解しているが、1つの小さな部分があなたを困惑させている機能を分解するための優れた方法は、Pythonデバッガーを使用することです. コメントを追加したものを次に示します。

-> def flatten(nested):
(Pdb) l
  1  -> def flatten(nested):
  2         try:
  3             for sublist in nested:
  4                 for element in flatten(sublist):
  5                     yield element
  6         except TypeError:
  7             yield nested
  8     
  9     import pdb; pdb.set_trace()
 10     list(flatten([[1,2],3]))
 11     
(Pdb) a
nested = [[1, 2], 3]

上記では、関数を入力したばかりで、引数は[[1, 2], 3]です。pdb のstep関数を使用して、遭遇する再帰呼び出しに関数をステップ実行しましょう。

(Pdb) s
> /Users/michael/foo.py(2)flatten()
-> try:
(Pdb) s
> /Users/michael/foo.py(3)flatten()
-> for sublist in nested:
(Pdb) s
> /Users/michael/foo.py(4)flatten()
-> for element in flatten(sublist):
(Pdb) s
--Call--
> /Users/michael/foo.py(1)flatten()
-> def flatten(nested):
(Pdb) a
nested = [1, 2]

flatten引数が である の1 つの内部フレームに足を踏み入れました[1, 2]

(Pdb) s
> /Users/michael/foo.py(2)flatten()
-> try:
(Pdb) s
> /Users/michael/foo.py(3)flatten()
-> for sublist in nested:
(Pdb) s
> /Users/michael/foo.py(4)flatten()
-> for element in flatten(sublist):
(Pdb) s
--Call--
> /Users/michael/foo.py(1)flatten()
-> def flatten(nested):
(Pdb) a
nested = 1

2 つのフレームで、引数1はもはや iterable ではありません。これは興味深いはずです…</p>

(Pdb) s
> /Users/michael/foo.py(2)flatten()
-> try:
(Pdb) s
> /Users/michael/foo.py(3)flatten()
-> for sublist in nested:
(Pdb) s
TypeError: "'int' object is not iterable"
> /Users/michael/foo.py(3)flatten()
-> for sublist in nested:
(Pdb) s
> /Users/michael/foo.py(6)flatten()
-> except TypeError:
(Pdb) s
> /Users/michael/foo.py(7)flatten()
-> yield nested
(Pdb) s
--Return--
> /Users/michael/foo.py(7)flatten()->1
-> yield nested

というexcept TypeErrorわけで、引数自体を生成しているだけです。フレームアップ!

(Pdb) s
> /Users/michael/foo.py(5)flatten()
-> yield element
(Pdb) l
  1     def flatten(nested):
  2         try:
  3             for sublist in nested:
  4                 for element in flatten(sublist):
  5  ->                 yield element
  6         except TypeError:
  7             yield nested
  8     
  9     import pdb; pdb.set_trace()
 10     list(flatten([[1,2],3]))
 11     

yield elementはもちろん yield1であるため、最下位のフレームが に達するTypeErrorと、結果はスタックの一番外側のフレームまで伝播しflatten、外側の iterable のさらに別の部分に移動する前に、結果が外の世界に渡されます。

于 2012-07-07T17:51:53.327 に答える
1

yield elementnestedがリストであるがsublistそうでない場合 (つまり、nestedが通常の「フラット」リストである場合) に実行できます。この場合、for sublist in nested正常に動作します。次の行が再帰的に呼び出さflatten sublistれると、再帰呼び出しが「サブリスト」(反復可能ではない) を反復しようとすると、typerror が発生します。この TypeError がキャッチされ、再帰呼び出しによって入力リスト全体が返されるため、呼び出しによって反復処理されfor element in flatten(sublist)ます。言い換えれば、サブリストがすでにフラットである場合に実行for element in flatten(sublist)することになります。for element in sublist

認識すべき重要なことは、ネストされていないリストでも再帰呼び出しになるということです。like の呼び出しflatten([1])2 つの yield[1]をもたらします: 再帰呼び出しは外側の呼び出しに移り、外側の呼び出しはすぐに re-yields になり1ます。

このバージョンの関数は、何が起こっているのかを理解するのに役立つ場合があります。

    def flatten(nested, indent=""):
        try:
            print indent, "Going to iterate over", nested
            for sublist in nested:
                print indent, "Going to iterate over flattening of", sublist
                for element in flatten(sublist, indent+"  "):
                    print indent, "Yielding", element
                    yield element
        except TypeError:
            print indent, "Type Error!  Yielding", nested
            yield nested

    >>> list(flatten([[1,2],3]))
     Going to iterate over [[1, 2], 3]
     Going to iterate over flattening of [1, 2]
       Going to iterate over [1, 2]
       Going to iterate over flattening of 1
         Going to iterate over 1
         Type Error!  Yielding 1
       Yielding 1
     Yielding 1
       Going to iterate over flattening of 2
         Going to iterate over 2
         Type Error!  Yielding 2
       Yielding 2
     Yielding 2
     Going to iterate over flattening of 3
       Going to iterate over 3
       Type Error!  Yielding 3
     Yielding 3
    [1, 2, 3]
于 2012-07-07T17:49:49.190 に答える
1

構造はtry exceptあなたのために例外をキャッチし、nestedに与えられた引数だけを返しますflatten()

したがって、 flatten(1) は でうまくいかず、その部分をfor sublist in nested:続行し、.exceptnested1

于 2012-07-07T17:39:38.717 に答える