简体   繁体   中英

Can Python list comprehension be used in this use-case?

Recently I had to traverse a tree, starting from an arbitrary node up to its root. I wrote something along the lines of

breadcrumbs = []
while node is not None:
    breadcrumbs.append(node.get_text())
    node = node.get_parent()

It has occured to me that it looks a lot like an ugly implementation of list construction that uses

result = []
while node is not None:
    result.append(node.get_text())
    node = node.next()

instead of the more idiomatic list comprehension

result = [node.get_text() for node in nodes]

This made me wonder whether there is a more idiomatic way of constructing a list from my tree traversal. For completeness (and because the above excerpts are mainly pseudo-code), here is the actual code I used:

model, treeiter = self.treeview.get_selection().get_selected()
breadcrumbs = []
while treeiter is not None:
    breadcrumbs.append(model.get_value(treeiter, self.COL_TEXT))
    treeiter = model.iter_parent(treeiter)
path = '/'.join(reversed(breadcrumbs))

For context, the first line calls Gtk.TreeSelection.get_selected (one of its return values is called iter in the API but has nothing to do with Python iterators). My code creates a string from the path selected by the user, eg, "A/b/2" in the example below:

+- A
|  |- a
|  \- b
|     |- 1
|     \- 2       <- selected element
\- B
   |- c
   \- d

You are true, the tree structure you are using is not very pythonic. But, you can easily turn it to a more pythonic solution using home-made generators .

For instance, to traverse the parents (or ancestors) you can define a iter_parents function like this:

def iter_parents(node):
    while node is not None:
        yield node
        node = node.get_parent()

With this in hand, you can build the breadcrumbs like this:

breadcrumbs = [node.get_text() for node in iter_parents(branch)]

Here is a small demo:

class Node(object):
    def __init__(self, text, parent=None):
        self._text = text
        self._parent = parent

    def get_text(self):
        return self._text

    def get_parent(self):
        return self._parent


branch = Node("c", Node("b", Node("a")))

breadcrumbs = [node.get_text() for node in iter_parents(branch)]

print(breadcrumbs)
# -> ['c', 'b', 'a']

For node.next() you can define a iter_next() generator the same way.

edit

Use reverse to reverse the list, for instance:

breadcrumbs = [node.get_text() for node in iter_parents(branch)]
breadcrumbs.reverse()

print(breadcrumbs)
# -> ['a', 'b', 'c']

Sure, you can wrap your while loop in a generator like this:

def get_parents(node):
    while node is not None:
        yield node
        node = node.get_parent()

Then you can use:

result = [node.get_text() for node in get_parents(nodes)]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM