[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.python

__iter__ yield

duccio

3/9/2008 8:58:00 PM


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=3DNone):
self.childs=3D[]
self.data=3Ddata
def appendNode(self, n):
node=3DNode(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=3DNode()
n.appendNode(1).appendNode(2).appendNode(3).appendNode(4)
n.appendNode(11).appendNode(22).appendNode(33).appendNode(44)
for node in n:
print node
6 Answers

Diez B. Roggisch

3/9/2008 10:00:00 PM

0

duccio schrieb:
>
> 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

Nope. There have been numerous discussions about this, introducing
something like a yield_all-keyword or such thing that would replace the
above boilerplate - but so far they all have been rejected. Search the
archives for the reasons, I don't remember them :)

Diez

Paul Hankin

3/9/2008 11:37:00 PM

0

On Mar 9, 8:58 pm, duccio <d...@tiscali.it> wrote:
> Someone knows if it's possible to make this __iter__ function with just  
> one 'yield' intead of two?
> ...
>      def __iter__(self):
>          yield self #1
>          for n in self.childs:
>              for nn in n.__iter__():
>                  yield nn #2

Only one yield and shorter (but not really any simpler):

from itertools import chain

class Node:
...
def __iter__(self):
for x in chain([self], *self.childs):
yield x

--
Paul Hankin

George Sakkis

3/10/2008 3:13:00 AM

0

On Mar 9, 7:37 pm, Paul Hankin <paul.han...@gmail.com> wrote:
> On Mar 9, 8:58 pm, duccio <d...@tiscali.it> wrote:
>
> > Someone knows if it's possible to make this __iter__ function with just
> > one 'yield' intead of two?
> > ...
> > def __iter__(self):
> > yield self #1
> > for n in self.childs:
> > for nn in n.__iter__():
> > yield nn #2
>
> Only one yield and shorter (but not really any simpler):
>
> from itertools import chain
>
> class Node:
> ...
> def __iter__(self):
> for x in chain([self], *self.childs):
> yield x

Actually this doesn't need a yield at all:

class Node:
...
def __iter__(self):
return chain([self], *self.childs)

George

Paul Hankin

3/10/2008 8:33:00 AM

0

On Mar 10, 3:12 am, George Sakkis <george.sak...@gmail.com> wrote:
> On Mar 9, 7:37 pm, Paul Hankin <paul.han...@gmail.com> wrote:
>
>
>
> > On Mar 9, 8:58 pm, duccio <d...@tiscali.it> wrote:
>
> > > Someone knows if it's possible to make this __iter__ function with just
> > > one 'yield' intead of two?
> > > ...
> > >      def __iter__(self):
> > >          yield self #1
> > >          for n in self.childs:
> > >              for nn in n.__iter__():
> > >                  yield nn #2
>
> > Only one yield and shorter (but not really any simpler):
>
> > from itertools import chain
>
> > class Node:
> >     ...
> >     def __iter__(self):
> >         for x in chain([self], *self.childs):
> >             yield x
>
> Actually this doesn't need a yield at all:
>
> class Node:
>     ...
>     def __iter__(self):
>         return chain([self], *self.childs)

The two have slightly different behaviours: without the yield, iter is
called immediately on every node in the tree as the iterators are
built. With yield, iterators are built lazily, giving better
behaviour.

But perhaps it's a defect of chain that it calls iter on all of its
arguments straight away -- would it be better if it only built the
iterators when they're needed?

--
Paul Hankin

Boris Borcic

3/10/2008 5:51:00 PM

0

Paul Hankin wrote:
> On Mar 10, 3:12 am, George Sakkis <george.sak...@gmail.com> wrote:
>> On Mar 9, 7:37 pm, Paul Hankin <paul.han...@gmail.com> wrote:
>>
>>
>>
>>> On Mar 9, 8:58 pm, duccio <d...@tiscali.it> wrote:
>>>> Someone knows if it's possible to make this __iter__ function with just
>>>> one 'yield' intead of two?
>>>> ...
>>>> def __iter__(self):
>>>> yield self #1
>>>> for n in self.childs:
>>>> for nn in n.__iter__():
>>>> yield nn #2
>>> Only one yield and shorter (but not really any simpler):
>>> from itertools import chain
>>> class Node:
>>> ...
>>> def __iter__(self):
>>> for x in chain([self], *self.childs):
>>> yield x
>> Actually this doesn't need a yield at all:
>>
>> class Node:
>> ...
>> def __iter__(self):
>> return chain([self], *self.childs)
>
> The two have slightly different behaviours: without the yield, iter is
> called immediately on every node in the tree as the iterators are
> built. With yield, iterators are built lazily, giving better
> behaviour.

generator expressions allow to save on the 'yield' while keeping the lazy
behavior, with either :

def __iter__(self):
return chain([self],(y for x in self.childs for y in x))

or

def __iter__(self):
return (y for x in chain([[self]],self.childs) for y in x)

BB

Lie Ryan

3/11/2008 3:59:00 PM

0

On Mar 10, 3:58 am, duccio <d...@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.