Recursive generator for flattening nested lists

前端 未结 5 1552
离开以前
离开以前 2020-12-03 12:04

I\'m a programming newbie and am having some trouble understanding an example from my python textbook (\"Beginning Python\" by Magnus Lie Hetland). The example is for a recu

5条回答
  •  借酒劲吻你
    2020-12-03 12:34

    A great way to break down a function that you generally understand, but one little part is stumping you, is to use the python debugger. Here it is with comments added:

    -> 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]
    

    Above, we've just entered the function and the argument is [[1, 2], 3]. Let's use pdb's step function to step through the function into any recursive calls we should encounter:

    (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]
    

    We've stepped into one inner frame of flatten, where the argument is [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
    

    Two frames in, the argument 1 isn't an iterable anymore. This should be interesting…

    (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
    

    OK, so because of the except TypeError, we're just yielding the argument itself. Up a frame!

    (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 will of course yield 1, so once our lowest frame hits a TypeError, the result propagates all the way up the stack to the outermost frame of flatten, which yields it to the outside world before moving on to further parts of the outer iterable.

提交回复
热议问题