__iter__ yield

Lie Lie.1296 at gmail.com
Tue Mar 11 16:58:37 CET 2008


On Mar 10, 3:58 am, duccio <d... at tiscali.it> wrote:
> Hello!
> Someone knows if it's possible to make this __iter__ function with just
> one 'yield' intead of two?
> Is there some simpler way to make this  __iter__ iter through all nodes?
> Thanks!
>
> class Node:
>      def __init__(self, data=None):
>          self.childs=[]
>          self.data=data
>      def appendNode(self, n):
>          node=Node(n)
>          self.childs.append(node)
>          return node
>      def __str__(self):
>          return '<'+str(self.data)+'>'
>      def __iter__(self):
>          yield self #1
>          for n in self.childs:
>              for nn in n.__iter__():
>                  yield nn #2
>
> n=Node()
> n.appendNode(1).appendNode(2).appendNode(3).appendNode(4)
> n.appendNode(11).appendNode(22).appendNode(33).appendNode(44)
> for node in n:
>      print node

Technically, the root node isn't a child node, and thus it shouldn't
show up in the iteration. I think a more semantically correct way for
this is to have the __str__() returns the current Node + All
Descendants Nodes (like the one you wanted for __iter__) and while
__iter__ only yields the child nodes (used by the __str__ to iterate
through itself), like this:

    class Node:
        def __init__(self, data=None):
            self.childs=[]
            self.data=data
        def appendNode(self, n):
            node=Node(n)
            self.childs.append(node)
            return node
        def __str__(self):
            ## Returns root node + all descendants
            return '<%s>\n' % self.data +
                   ''.join(str(child) for child in self)
        def __iter__(self):
            ## yields childrens only
            for n in self.childs:
                yield n

    n=Node()
    n.appendNode(1).appendNode(2).appendNode(3).appendNode(4)
    ## Note I added the line below for testing branches in
    ## lower nodes
    n.childs[0].appendNode(222)
    n.appendNode(11).appendNode(22).appendNode(33).appendNode(44)
    print n

The missing functionality of returning current Node's name can be
easily solved by adding it in another function.

The main problem with __iter__ behavior you originally wanted is that
it doesn't reflect the nesting behavior of the Node class and you
could've been equally well served by using flat data structure if you
do that. I smell a bad data structure design here, you'd better revise
your design.

To reflect the nesting, you could do it like this:

class Node:
    def __init__(self, data=None):
        self.childs=[]
        self.data=data
    def appendNode(self, n):
        node=Node(n)
        self.childs.append(node)
        return node
    def __str__(self):
        ## This reflects nesting behavior better
        return '\n<%s>' % (str(self.data) +
               ''.join(str(child) for child in self))

## Uncomment this and Comment the statement above
## for an alternate data structure you might be
## interested in, the format below resembles HTML
##        curNode = str(self.data)
##        return '\n<%s>%s\n</%s>' % (
##                curNode,
##                ''.join(str(child) for child in self),
##                curNode)

    def __iter__(self):
        for n in self.childs:
            yield n

n=Node()
n.appendNode(1).appendNode(2).appendNode(3).appendNode(4)
n.childs[0].appendNode(222)
n.appendNode(11).appendNode(22).appendNode(33).appendNode(44)
print n


This changes the data structure quite a lot though, but the data
structure is bad from the start.



More information about the Python-list mailing list