Re: [Tutor] Here is newbie doc on how to implement generators

2007-07-23 Thread Dave Kuhlman
On Mon, Jul 16, 2007 at 06:13:35PM -0400, Kent Johnson wrote:

Kent -

Rather than try to reply in detail to your suggestions, I've tried
to ammend my document to reflect your comments.

Thanks again for the help.

Dave

[good suggestions and corrections from Kent, snipped]


-- 
Dave Kuhlman
http://www.rexx.com/~dkuhlman
___
Tutor maillist  -  Tutor@python.org
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Here is newbie doc on how to implement generators

2007-07-16 Thread Kent Johnson
Dave Kuhlman wrote:
> I find iterators and generators fascinating.  So, in order to try
> to understand them better myself, I've written up some notes.  I'm
> hoping that these notes might help someone new to the generators
> and iterators in Python.  You can find it here:
> 
> http://www.rexx.com/~dkuhlman/python_comments.html
> 
> http://www.rexx.com/~dkuhlman/python_comments.html#iterators-and-generators
> 
> I'll appreciate any comments and suggestions that might help me
> improve it.

In the Consumers section, the first time you mention the iterator 
protocol, you omit mention of __init__() - this is a required method of 
an iterator.

In the section "The iterator protocol", the __iter__() bullet, "(2) 
return the value returned by a generator method" is not correct. An 
iterator must return self from __iter__(). An object that returns a 
(new) generator from __iter__() is an iterable, not an iterator.

In some cases there is no need to write a separate generator method and 
call it in __iter__(); __iter__() itself can be written as a generator 
method using yield. This works if the generator method doesn't need any 
arguments.

Your Doubler class will not behave correctly because of the 
re-initialization of the index in __iter__(). Calling __iter__() should 
not have any side effects. Here is an example of why this is a problem, 
using Doubler as you have written it:

In [9]: d=Doubler(range(5))
In [10]: d.next()
Out[10]: 0
In [11]: d.next()
Out[11]: 2
In [12]: for i in d: print i
:
0
2
4
6
8

Notice how the for loop resets the iterator (because it calls __iter__() 
to make sure it has an iterator). Compare with a correctly implemented 
iterator:

In [13]: it = iter(range(5))
In [14]: it.next()
Out[14]: 0
In [15]: it.next()
Out[15]: 1
In [16]: for i in it: print i
:
2
3
4

Double would actually be easier to implement with __iter__() as a 
generator method.

I already commented on the pitfalls of making an object its own 
iterator. In the standard library, a file is its own iterator. That 
makes sense because it is a wrapper around a singleton state - the seek 
position of the file. I don't think it makes much sense in general to 
have an object be its own iterator unless the object is just an iterator.

Another reference is the What's New doc for Python 2.2:
http://www.python.org/doc/2.2.3/whatsnew/node4.html
and the following page.

Kent
___
Tutor maillist  -  Tutor@python.org
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Here is newbie doc on how to implement generators

2007-07-16 Thread Kent Johnson
Dave Kuhlman wrote:
> On Fri, Jul 13, 2007 at 12:39:40PM +1200, John Fouhy wrote:
>> On 13/07/07, Dave Kuhlman <[EMAIL PROTECTED]> wrote:
>>> And, I have a question -- If you look at the example of the
>>> iterative (non-recursive) generator (the Doubler class), you will
>>> see that it walks a list, not a tree.  That's because I was *not*
>>> able to figure out how to implement a non-recursive tree walk
>>> generator.
>> You should just be able to use a stack -- push the children onto a
>> stack, then pop them off and walk through them.
>>
>> Here's an example, using tuples and lists as a basic tree data type:
>>
>> ### treetest.py #
>>
>> TREE = (0, [(1, [(2, [(3, []),
>>   (4, []),
>>   (5, [(6, []),
>>(7, []),
>>(8, []),
>>(9, []),
>>]),
>>   (10, [(11, []),
>> (12, []),
>> ]),
>>   ]),
>>  (13, [(14, []),
>>(15, []),
>>(16, [])]),
>>  (17, []),
>>  (18, []),
>>  ]),
>> (19, []),
>> (20, []),
>> ])
>>
>> def walkTree(tree):
>> # stack to hold nodes as we walk through
>> stack = []
>> stack.append(tree)
>>
>> while stack:
>> value, children = stack.pop()
>> for child in reversed(children):  # reverse children to get
>> the right order.
>> stack.append(child)
>> yield value
>>
>> if __name__ == '__main__':
>> for value in walkTree(TREE):
>> print value
>>
>> ### treetest.py #
> 
> John -
> 
> That is so cool.  I can't believe that it is so simple and elegant. 
> I thought that a stack was involved in the solution, but I could
> not figure out how to use it.  Thanks.
> 
> And, to extend this a bit more, here are two slightly modified
> versions of your solution that implement classes whose
> instances are iterators.
> 
> 
> #
> # Version #1 -- This version has a next() method, as required by
> #   the iterator protocol.
> #
> class Node(object):
> def __init__(self, value='', children=None):
> self.value = chr(value + 97) * 3
> if children is None:
> children = []
> else:
> self.children = children
> def walk_tree(self):
> # stack to hold nodes as we walk through
> stack = []
> stack.append(self)
> while stack:
> node = stack.pop()
> # reverse children to get the right order.
> stack.extend(reversed(node.children))
> yield node
> def __iter__(self):
> self.iterator = self.walk_tree()
> return self
> def next(self):
> return self.iterator.next()
> 
> 
> #
> # Version #2 -- This version does not have a next() method, but
> #   the iterators returned by __iter__() do have a next().
> #
> class Node(object):
> def __init__(self, value='', children=None):
> self.value = chr(value + 97) * 3
> if children is None:
> children = []
> else:
> self.children = children
> def walk_tree(self):
> # stack to hold nodes as we walk through
> stack = []
> stack.append(self)
> while stack:
> node = stack.pop()
> # reverse children to get the right order.
> stack.extend(reversed(node.children))
> yield node
> def __iter__(self):
> return self.walk_tree()

I think Version 2 is preferable. Not only is it shorter, but it is 
safer. Version 1 has essentially a singleton iterator so any code that 
tries to iterate the same node more than once will fail.  For example 
multi-threaded code, or perhaps during an iteration there could be a 
reason to start a new iteration.

Kent
___
Tutor maillist  -  Tutor@python.org
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Here is newbie doc on how to implement generators

2007-07-16 Thread Kent Johnson
John Fouhy wrote:
> def walkTree(tree):
> # stack to hold nodes as we walk through
> stack = []
> stack.append(tree)
> 
> while stack:
> value, children = stack.pop()
> for child in reversed(children):  # reverse children to get
> the right order.
> stack.append(child)

FWIW this could be written as
   stack.extend(reversed(children))

Kent
___
Tutor maillist  -  Tutor@python.org
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Here is newbie doc on how to implement generators

2007-07-13 Thread Dave Kuhlman
On Fri, Jul 13, 2007 at 12:39:40PM +1200, John Fouhy wrote:
> On 13/07/07, Dave Kuhlman <[EMAIL PROTECTED]> wrote:
> > And, I have a question -- If you look at the example of the
> > iterative (non-recursive) generator (the Doubler class), you will
> > see that it walks a list, not a tree.  That's because I was *not*
> > able to figure out how to implement a non-recursive tree walk
> > generator.
> 
> You should just be able to use a stack -- push the children onto a
> stack, then pop them off and walk through them.
> 
> Here's an example, using tuples and lists as a basic tree data type:
> 
> ### treetest.py #
> 
> TREE = (0, [(1, [(2, [(3, []),
>   (4, []),
>   (5, [(6, []),
>(7, []),
>(8, []),
>(9, []),
>]),
>   (10, [(11, []),
> (12, []),
> ]),
>   ]),
>  (13, [(14, []),
>(15, []),
>(16, [])]),
>  (17, []),
>  (18, []),
>  ]),
> (19, []),
> (20, []),
> ])
> 
> def walkTree(tree):
> # stack to hold nodes as we walk through
> stack = []
> stack.append(tree)
> 
> while stack:
> value, children = stack.pop()
> for child in reversed(children):  # reverse children to get
> the right order.
> stack.append(child)
> yield value
> 
> if __name__ == '__main__':
> for value in walkTree(TREE):
> print value
> 
> ### treetest.py #

John -

That is so cool.  I can't believe that it is so simple and elegant. 
I thought that a stack was involved in the solution, but I could
not figure out how to use it.  Thanks.

And, to extend this a bit more, here are two slightly modified
versions of your solution that implement classes whose
instances are iterators.


#
# Version #1 -- This version has a next() method, as required by
#   the iterator protocol.
#
class Node(object):
def __init__(self, value='', children=None):
self.value = chr(value + 97) * 3
if children is None:
children = []
else:
self.children = children
def walk_tree(self):
# stack to hold nodes as we walk through
stack = []
stack.append(self)
while stack:
node = stack.pop()
# reverse children to get the right order.
stack.extend(reversed(node.children))
yield node
def __iter__(self):
self.iterator = self.walk_tree()
return self
def next(self):
return self.iterator.next()


#
# Version #2 -- This version does not have a next() method, but
#   the iterators returned by __iter__() do have a next().
#
class Node(object):
def __init__(self, value='', children=None):
self.value = chr(value + 97) * 3
if children is None:
children = []
else:
self.children = children
def walk_tree(self):
# stack to hold nodes as we walk through
stack = []
stack.append(self)
while stack:
node = stack.pop()
# reverse children to get the right order.
stack.extend(reversed(node.children))
yield node
def __iter__(self):
return self.walk_tree()


#
# Create some data.
#
TREE = Node(0, [Node(1, [Node(2, [Node(3, []),
  Node(4, []),
  Node(5, [Node(6, []),
   Node(7, []),
   Node(8, []),
   Node(9, []),
   ]),
  Node(10, [Node(11, []),
Node(12, []),
]),
  ]),
 Node(13, [Node(14, []),
   Node(15, []),
   Node(16, [])]),
 Node(17, []),
 Node(18, []),
 ]),
Node(19, []),
Node(20, []),
])


#
# Here is a function to exercise the iterator.
#
def test():
for node in TREE:
print 'value: %s' % (node.value, )

Dave



-- 
Dave Kuhlman
http://www.rexx.com/~dkuhlman
___
Tutor maillist  -  Tutor@python.org
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Here is newbie doc on how to implement generators

2007-07-13 Thread Eric Brunson
Dave Kuhlman wrote:
> I find iterators and generators fascinating.  So, in order to try
> to understand them better myself, I've written up some notes.  I'm
> hoping that these notes might help someone new to the generators
> and iterators in Python.  You can find it here:
>
> http://www.rexx.com/~dkuhlman/python_comments.html
> 
> http://www.rexx.com/~dkuhlman/python_comments.html#iterators-and-generators
>
> I'll appreciate any comments and suggestions that might help me
> improve it.
>
> Please pass the above link along to anyone you think it might help.
>
> And, I have a question -- If you look at the example of the
> iterative (non-recursive) generator (the Doubler class), you will
> see that it walks a list, not a tree.  That's because I was *not*
> able to figure out how to implement a non-recursive tree walk
> generator.
>
> I found examples showing non-recursive/iterative solutions to how
> to walk a *binary* tree.  Those examples are at:
>
> Lib/test/test_generator.py   # in the Python distribution
> http://en.wikipedia.org/wiki/Inorder_tree_walk
>
> But, I could not find a example that shows a non-recursive way to
> walk a tree in which each node has an arbitrary number of children.
>   

If you store your tree data in an adjacency list iteration becomes trivial.

> Alas, I could not write that non-recursive tree walk.  The recusive
> walk is easy and clean.  So, maybe I should not worry about the
> non-recursive approach.  Still, it really bothers me that I could
> not do it.
>
> So, if you know where there are some examples, that would help me
> improve my notes on iterators and generators, please give me a
> link.
>
> Dave
>
>
>
>   

___
Tutor maillist  -  Tutor@python.org
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Here is newbie doc on how to implement generators

2007-07-12 Thread John Fouhy
On 13/07/07, Dave Kuhlman <[EMAIL PROTECTED]> wrote:
> And, I have a question -- If you look at the example of the
> iterative (non-recursive) generator (the Doubler class), you will
> see that it walks a list, not a tree.  That's because I was *not*
> able to figure out how to implement a non-recursive tree walk
> generator.

You should just be able to use a stack -- push the children onto a
stack, then pop them off and walk through them.

Here's an example, using tuples and lists as a basic tree data type:

### treetest.py #

TREE = (0, [(1, [(2, [(3, []),
  (4, []),
  (5, [(6, []),
   (7, []),
   (8, []),
   (9, []),
   ]),
  (10, [(11, []),
(12, []),
]),
  ]),
 (13, [(14, []),
   (15, []),
   (16, [])]),
 (17, []),
 (18, []),
 ]),
(19, []),
(20, []),
])

def walkTree(tree):
# stack to hold nodes as we walk through
stack = []
stack.append(tree)

while stack:
value, children = stack.pop()
for child in reversed(children):  # reverse children to get
the right order.
stack.append(child)
yield value

if __name__ == '__main__':
for value in walkTree(TREE):
print value

### treetest.py #

HTH!

-- 
John.
___
Tutor maillist  -  Tutor@python.org
http://mail.python.org/mailman/listinfo/tutor


[Tutor] Here is newbie doc on how to implement generators

2007-07-12 Thread Dave Kuhlman
I find iterators and generators fascinating.  So, in order to try
to understand them better myself, I've written up some notes.  I'm
hoping that these notes might help someone new to the generators
and iterators in Python.  You can find it here:

http://www.rexx.com/~dkuhlman/python_comments.html
http://www.rexx.com/~dkuhlman/python_comments.html#iterators-and-generators

I'll appreciate any comments and suggestions that might help me
improve it.

Please pass the above link along to anyone you think it might help.

And, I have a question -- If you look at the example of the
iterative (non-recursive) generator (the Doubler class), you will
see that it walks a list, not a tree.  That's because I was *not*
able to figure out how to implement a non-recursive tree walk
generator.

I found examples showing non-recursive/iterative solutions to how
to walk a *binary* tree.  Those examples are at:

Lib/test/test_generator.py   # in the Python distribution
http://en.wikipedia.org/wiki/Inorder_tree_walk

But, I could not find a example that shows a non-recursive way to
walk a tree in which each node has an arbitrary number of children.

Alas, I could not write that non-recursive tree walk.  The recusive
walk is easy and clean.  So, maybe I should not worry about the
non-recursive approach.  Still, it really bothers me that I could
not do it.

So, if you know where there are some examples, that would help me
improve my notes on iterators and generators, please give me a
link.

Dave



-- 
Dave Kuhlman
http://www.rexx.com/~dkuhlman
___
Tutor maillist  -  Tutor@python.org
http://mail.python.org/mailman/listinfo/tutor