Idiomatic python: distribution or smoothing?

I am writing a width function to traverse a tree in depth, and I want to do this:

def traverse(node):
    yield node
    for n in node.children:
        yield_all traverse(n) # << if Python had a yield_all statement

The idea is to complete a (flat) sequence of nodes in a tree.

Approach # 1: (distributed outputs)

def traverse(node):
    yield node
    for n in node.children:
        for m in traverse(n):
            yield m

Approach No. 2: (smoothing sequences)

def traverse(node):
    return itertools.chain([node],*(traverse(n) for n in node.children))

The first approach seems cleaner, but I feel weirdly explicitly yieldentering each node in a subtree at each level.

The second approach is concise and slightly dirty, but it corresponds to what I would write in Haskell:

traverse node = node : concatMap traverse (children node)

So my question is: which is better? Or did I miss the best third option?

+5
source share
4 answers

[] . PEP-380, , Python 3.3 yield from:

def traverse(node):
    yield node
    for n in node.children:
        yield from traverse(n)
+4

. .: -)

+3

It is a matter of opinion, so all answers will be only value judgments. As far as I can tell, there is no elegant third way.

My opinion is that the first method wins. This is clearer and clearer - Python is not Haskell, although it can do some functional things, and often the functional approach just doesn't look so neat.

+1
source

Moving by node position:

def iter_tree(t, i=0, j=0):
    yield (i, j), t
    for j, n in enumerate(t.children):
        yield from iter_tree(n, i + 1, j)

for (i, j), n in iter_tree(t):
    print(i*'    ', (i, j), n)
0
source

All Articles