Re: [Tutor] I am having difficulty grasping 'generators'

2014-05-28 Thread Cameron Simpson

On 27May2014 15:27, Degreat Yartey yarteydegre...@gmail.com wrote:

I am studying python on my own (i.e. i am between the beginner and
intermediate level) and i haven't met any difficulty until i reached the
topic 'Generators and Iterators'.
I need an explanation so simple as using the expression 'print ()', in this
case 'yield'.
Python 2.6 here!
Thank you.


Generators are functions that do a bit of work and then yield a value, then a 
bit more and so on. This means that you call them once. What you get back is 
an iterator, not the normal function return value.


Whenever you use the iterator, the generator function runs until it hits a 
yield statement, and the value in theyield statement is what you get for that 
iteration. Next time you iterate, the function runs a bit more, until it yields 
again, or returns (end of function, and that causes end of iteration).


So the function doesn't even run until you ask for a value, and then it only 
runs long enough to find the next value.


Example (all code illstrative only, untested):

Suppose you need to process every second line of a file.

You might write it directly like this:

  def munge_lines(fp):
''' Do stuff with every second line of the already-open file `fp`.
'''
lineno = 0
for line in fp:
  lineno += 1
  if lineno % 2 == 0:
print lineno, line,

That should read lines from the file and print every second one with the line 
number.


Now suppose you want something more complex than every second line, 
especially something that requires keeping track of some state. In the example 
above you only need the line number, and using it still consumes 2 of the 3 
lines in the loop body.


A more common example might be lines between two markers.

The more of that you embed in the munge_lines function, the more it will get 
in the way of seeing what the function actually does.


So a reasonable thing might be to write a function that gets the requested 
lines:


  def wanted_lines(fp):
wanted = []
between = False
for line in fp:
  if between:
if 'end_marker' in line:
  between = False
else:
  wanted.append(line)
  elif 'start_maker' in line:
between = True
return wanted

This reads the whole file and returns a line of the wanted lines, and 
munge_lines: might then look like this:


  for line in wanted_lines(fp):
print line

However:

  - that reads the whole file before returning anything

  - has to keep all the lines in the list wanted

Slow in response, heavy in memory cost, and unworkable if fp actually doesn't 
end (eg reading from a terminal, or a pipeline, or...)


What you'd really like is to get each line as needed.

We can rewrite wanted_lines as a generator:

  def wanted_lines(fp):
between = False
for line in fp:
  if between:
if 'end_marker' in line:
  between = False
else:
  yield line
  elif 'start_maker' in line:
between = True

All we've done is used yield instead of the append and removed the wanted 
list and the return statement. The calling code is the same.


To see the difference, put a print in wanted_lines as the first line of the 
for loop. With the list version you will see all the prints run before you 
get the array back. With the generator you will see the print run just before 
each value you get back.


Cheers,
Cameron Simpson c...@zip.com.au
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] I am having difficulty grasping 'generators'

2014-05-28 Thread Degreat Yartey
I am completely new to programming!
On May 27, 2014 10:54 PM, R. Alan Monroe amon...@columbus.rr.com wrote:

  I need an explanation so simple as using the expression 'print ()', in
 this case 'yield'.
  Python 2.6 here!

 Ever write any C programs with static variables? Generators can be
 explained in those terms if you have experience with them.

 Alan

 ___
 Tutor maillist  -  Tutor@python.org
 To unsubscribe or change subscription options:
 https://mail.python.org/mailman/listinfo/tutor

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] I am having difficulty grasping 'generators'

2014-05-28 Thread Degreat Yartey
I really love this explanation... that means functions just run till it
finishes its duty, then return...and generators just generate one at a time
until the 'for' statement asks for __next__().
On May 28, 2014 8:37 AM, Cameron Simpson c...@zip.com.au wrote:

 On 27May2014 15:27, Degreat Yartey yarteydegre...@gmail.com wrote:

 I am studying python on my own (i.e. i am between the beginner and
 intermediate level) and i haven't met any difficulty until i reached the
 topic 'Generators and Iterators'.
 I need an explanation so simple as using the expression 'print ()', in
 this
 case 'yield'.
 Python 2.6 here!
 Thank you.


 Generators are functions that do a bit of work and then yield a value,
 then a bit more and so on. This means that you call them once. What you
 get back is an iterator, not the normal function return value.

 Whenever you use the iterator, the generator function runs until it hits a
 yield statement, and the value in theyield statement is what you get for
 that iteration. Next time you iterate, the function runs a bit more, until
 it yields again, or returns (end of function, and that causes end of
 iteration).

 So the function doesn't even run until you ask for a value, and then it
 only runs long enough to find the next value.

 Example (all code illstrative only, untested):

 Suppose you need to process every second line of a file.

 You might write it directly like this:

   def munge_lines(fp):
 ''' Do stuff with every second line of the already-open file `fp`.
 '''
 lineno = 0
 for line in fp:
   lineno += 1
   if lineno % 2 == 0:
 print lineno, line,

 That should read lines from the file and print every second one with the
 line number.

 Now suppose you want something more complex than every second line,
 especially something that requires keeping track of some state. In the
 example above you only need the line number, and using it still consumes 2
 of the 3 lines in the loop body.

 A more common example might be lines between two markers.

 The more of that you embed in the munge_lines function, the more it will
 get in the way of seeing what the function actually does.

 So a reasonable thing might be to write a function that gets the requested
 lines:

   def wanted_lines(fp):
 wanted = []
 between = False
 for line in fp:
   if between:
 if 'end_marker' in line:
   between = False
 else:
   wanted.append(line)
   elif 'start_maker' in line:
 between = True
 return wanted

 This reads the whole file and returns a line of the wanted lines, and
 munge_lines: might then look like this:

   for line in wanted_lines(fp):
 print line

 However:

   - that reads the whole file before returning anything

   - has to keep all the lines in the list wanted

 Slow in response, heavy in memory cost, and unworkable if fp actually
 doesn't end (eg reading from a terminal, or a pipeline, or...)

 What you'd really like is to get each line as needed.

 We can rewrite wanted_lines as a generator:

   def wanted_lines(fp):
 between = False
 for line in fp:
   if between:
 if 'end_marker' in line:
   between = False
 else:
   yield line
   elif 'start_maker' in line:
 between = True

 All we've done is used yield instead of the append and removed the
 wanted list and the return statement. The calling code is the same.

 To see the difference, put a print in wanted_lines as the first line
 of the for loop. With the list version you will see all the prints run
 before you get the array back. With the generator you will see the print
 run just before each value you get back.

 Cheers,
 Cameron Simpson c...@zip.com.au
 ___
 Tutor maillist  -  Tutor@python.org
 To unsubscribe or change subscription options:
 https://mail.python.org/mailman/listinfo/tutor

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] I am having difficulty grasping 'generators'

2014-05-28 Thread Degreat Yartey
This means that  '...' should generally contain a manipulator then yield
generates from where it stopped...*getting it*
Thanks for the explanation though! Its seems so simple to digest. Thank
you...
On May 28, 2014 1:09 AM, Danny Yoo d...@hashcollision.org wrote:

 On Tue, May 27, 2014 at 12:27 PM, Degreat Yartey
 yarteydegre...@gmail.com wrote:
  I am studying python on my own (i.e. i am between the beginner and
  intermediate level) and i haven't met any difficulty until i reached the
  topic 'Generators and Iterators'.
  I need an explanation so simple as using the expression 'print ()', in
 this
  case 'yield'.


 You can think of a generator as almost like a function, except it can
 return, not just once, but multiple times.


 Because it can return multiple times, if we squint at it enough, it
 acts like a _sequence_, just like the other sequence-like things in
 Python like files and lists and tuples.  That is, as a sequence, it's
 something that we can walk down, element by element.  We can loop over
 it.


 For example, let's say that we wanted to represent the same sequences
 as that of range(5).  Here's one way we can do it with a generator:

 #
 def upToFive():
 yield 0
 yield 1
 yield 2
 yield 3
 yield 4
 #


 Let's try it.

 #
  sequence = upToFive()
  next(sequence)
 0
  next(sequence)
 1
  next(sequence)
 2
  next(sequence)
 3
  next(sequence)
 4
  next(sequence)
 Traceback (most recent call last):
   File stdin, line 1, in module
 StopIteration
 
 
  for x in upToFive():
 ... print(I see %d % x)
 ...
 I see 0
 I see 1
 I see 2
 I see 3
 I see 4
 #


 Now this is a toy example.  If we wanted range(5), we'd just say
 range(5) and be done with it.


 What's neat about generators is that they make it easy to build these
 sequences while pretending that we're writing a plain function.  All
 of the even numbers, for examples, is a sequence that we can make with
 a generator:

 #
 def onlyEvens():
 n = 0
 while True:
 yield n
 n = n + 2
 #


 Let's try running it:

 #
  sequence = onlyEvens()
  next(sequence)
 0
  next(sequence)
 2
  next(sequence)
 4
  next(sequence)
 6
 #

 And note that this sequence doesn't stop!  We can keep calling next()
 on it and it will continue to run.


 We _can_ write a loop to run over such infinite sequences, but we'll
 also have to make sure to stop it manually: it won't exhaust
 otherwise, so doing something like:

 #
 for n in onlyEvens():
 ...
 #

 better have something in the ... that interrupts or returns, or else
 that loop will never end.

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] I am having difficulty grasping 'generators'

2014-05-28 Thread Alan Gauld

On 28/05/14 11:52, Degreat Yartey wrote:

This means that  '...' should generally contain a manipulator then yield
generates from where it stopped...*getting it*



It would help if you deleted the irrelevent bits so we can see
which '...' you mean.

I'm guessing it was this comment, right at the end, by Danny:


We _can_ write a loop to run over such infinite sequences, but we'll
also have to make sure to stop it manually: it won't exhaust
otherwise, so doing something like:

#
for n in onlyEvens():
 ...
#

better have something in the ... that interrupts or returns, or else
that loop will never end.



Notice that the ... here is not part of the generator.
So the ... here references to how you process the output of the 
generator - the yielded values. So in this example case you'd

have something like

#
for n in onlyEvens():
if n  1000: break # prevent infinite loop
# now process the evens that we are interested in.
#

The 'break' could also be a 'return' if the loop were
inside a function.

And it doesn't have to be a direct value check as shown the
condition could be based on some external condition or even
user input. The important point is to make sure that you
have some way to exit the loop if the generator is infinite.

hth
--
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.flickr.com/photos/alangauldphotos

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] I am having difficulty grasping 'generators'

2014-05-28 Thread Hilton Fernandes
Dear all,

i'd like to thank every answer in this list. Alan Gauld is a fine writer of
excellent introductory material on Pyton, and so are a few other members of
this list.

So, it is always enlightening to read what you all write. Keep up the good
work.

All the best,
hilton



On Wed, May 28, 2014 at 8:57 AM, Alan Gauld alan.ga...@btinternet.comwrote:

 On 28/05/14 11:52, Degreat Yartey wrote:

 This means that  '...' should generally contain a manipulator then yield
 generates from where it stopped...*getting it*



 It would help if you deleted the irrelevent bits so we can see
 which '...' you mean.

 I'm guessing it was this comment, right at the end, by Danny:


  We _can_ write a loop to run over such infinite sequences, but we'll
 also have to make sure to stop it manually: it won't exhaust
 otherwise, so doing something like:

 #
 for n in onlyEvens():
  ...
 #

 better have something in the ... that interrupts or returns, or else
 that loop will never end.



 Notice that the ... here is not part of the generator.
 So the ... here references to how you process the output of the generator
 - the yielded values. So in this example case you'd
 have something like


 #
 for n in onlyEvens():
 if n  1000: break # prevent infinite loop
 # now process the evens that we are interested in.
 #

 The 'break' could also be a 'return' if the loop were
 inside a function.

 And it doesn't have to be a direct value check as shown the
 condition could be based on some external condition or even
 user input. The important point is to make sure that you
 have some way to exit the loop if the generator is infinite.

 hth
 --
 Alan G
 Author of the Learn to Program web site
 http://www.alan-g.me.uk/
 http://www.flickr.com/photos/alangauldphotos


 ___
 Tutor maillist  -  Tutor@python.org
 To unsubscribe or change subscription options:
 https://mail.python.org/mailman/listinfo/tutor

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] I am having difficulty grasping 'generators'

2014-05-28 Thread wesley chun
I'm not going to add too much more to all the replies here already, but one
of my students did record a quick 6-minute video in one of my courses
where i explained generators. hopefully you find it useful!

It's about halfway down the page at http://cyberwebconsulting.com. (Also
for those learning Python and in the San Francisco area, I'm offering
another intensive 3-day course mid-summer -- more info on the same page.
Ping me privately for more details or if you have questions!)

Cheers,
--Wesley


On Tue, May 27, 2014 at 12:27 PM, Degreat Yartey
yarteydegre...@gmail.comwrote:

 I am studying python on my own (i.e. i am between the beginner and
 intermediate level) and i haven't met any difficulty until i reached the
 topic 'Generators and Iterators'.
 I need an explanation so simple as using the expression 'print ()', in
 this case 'yield'.
 Python 2.6 here!
 Thank you.

 ___
 Tutor maillist  -  Tutor@python.org
 To unsubscribe or change subscription options:
 https://mail.python.org/mailman/listinfo/tutor




-- 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
A computer never does what you want... only what you tell it.
+wesley chun http://google.com/+WesleyChun : wescpy at gmail :
@wescpyhttp://twitter.com/wescpy
Python training  consulting : http://CyberwebConsulting.com
Core Python books : http://CorePython.com
Python blog: http://wescpy.blogspot.com
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] I am having difficulty grasping 'generators'

2014-05-27 Thread R. Alan Monroe
 I need an explanation so simple as using the expression 'print ()', in this 
 case 'yield'.
 Python 2.6 here!

Ever write any C programs with static variables? Generators can be
explained in those terms if you have experience with them.

Alan

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] I am having difficulty grasping 'generators'

2014-05-27 Thread Danny Yoo
On Tue, May 27, 2014 at 12:27 PM, Degreat Yartey
yarteydegre...@gmail.com wrote:
 I am studying python on my own (i.e. i am between the beginner and
 intermediate level) and i haven't met any difficulty until i reached the
 topic 'Generators and Iterators'.
 I need an explanation so simple as using the expression 'print ()', in this
 case 'yield'.


You can think of a generator as almost like a function, except it can
return, not just once, but multiple times.


Because it can return multiple times, if we squint at it enough, it
acts like a _sequence_, just like the other sequence-like things in
Python like files and lists and tuples.  That is, as a sequence, it's
something that we can walk down, element by element.  We can loop over
it.


For example, let's say that we wanted to represent the same sequences
as that of range(5).  Here's one way we can do it with a generator:

#
def upToFive():
yield 0
yield 1
yield 2
yield 3
yield 4
#


Let's try it.

#
 sequence = upToFive()
 next(sequence)
0
 next(sequence)
1
 next(sequence)
2
 next(sequence)
3
 next(sequence)
4
 next(sequence)
Traceback (most recent call last):
  File stdin, line 1, in module
StopIteration


 for x in upToFive():
... print(I see %d % x)
...
I see 0
I see 1
I see 2
I see 3
I see 4
#


Now this is a toy example.  If we wanted range(5), we'd just say
range(5) and be done with it.


What's neat about generators is that they make it easy to build these
sequences while pretending that we're writing a plain function.  All
of the even numbers, for examples, is a sequence that we can make with
a generator:

#
def onlyEvens():
n = 0
while True:
yield n
n = n + 2
#


Let's try running it:

#
 sequence = onlyEvens()
 next(sequence)
0
 next(sequence)
2
 next(sequence)
4
 next(sequence)
6
#

And note that this sequence doesn't stop!  We can keep calling next()
on it and it will continue to run.


We _can_ write a loop to run over such infinite sequences, but we'll
also have to make sure to stop it manually: it won't exhaust
otherwise, so doing something like:

#
for n in onlyEvens():
...
#

better have something in the ... that interrupts or returns, or else
that loop will never end.
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] I am having difficulty grasping 'generators'

2014-05-27 Thread Marc Tompkins
On Tue, May 27, 2014 at 12:27 PM, Degreat Yartey
yarteydegre...@gmail.com wrote:
 I am studying python on my own (i.e. i am between the beginner and
 intermediate level) and i haven't met any difficulty until i reached the
 topic 'Generators and Iterators'.
 I need an explanation so simple as using the expression 'print ()', in this
 case 'yield'.
 Python 2.6 here!
 Thank you.


Simply put, a generator is just like a function - except that a
function returns a value and then dies, but a generator yields a value
and sticks around, waiting to yield another one - and another, and
another, etc.

I had some difficulty grasping the concept of generators at first
because the examples you get in the tutorials are either trivial (you
could do the same thing some other way, and it would be simpler) or
horribly complex (and you get bogged down in the problem, rather than
getting to grips with generators themselves.)

So here's a real-life example - the first time I actually used
generators... and they actually saved me a LOT of hard work and
confusion.  I needed to read some database files (from a very old
database, for which I don't have an API - so I basically needed to
read each record and interpret it byte-by-byte).  I wrote a bunch of
classes for the different tables in the database (I'll use Patient()
as my example), but the heart of the operation is a generator.


def RecordGenerator(office=None, fileType=None):
obj = fTypes[fileType]()# create an empty record so we can
read its attributes
recLen = obj.RecordLength# such as the record length, the filename, etc.
with open(getattr(office, obj.TLA) + '.dat','rb') as inFile:
tmpIn = inFile.read()
buf = StringIO.StringIO(tmpIn)
inRec = buf.read(recLen)# initialize inRec and burn the
header record
while not (len(inRec)  recLen):
inRec = buf.read(recLen)
obj = fTypes[fileType](inRec)
if (obj.Valid):
yield obj
buf.close()

Now I'm writing a program that needs to process all the patients in
office 01.  I write the following:

for obj in RecordGenerator(01, Patient):
doStuffPart1
doStuffPart2
doStuffPart3
etc.

The generator creates an empty Patient() object, looks at its
attributes, and finds that the filename is 01PAT.dat and the records
are 1024 bytes long.  It opens the file and reads it into tmpIn, then
grabs 1024-byte chunks of tmpIn and passes them to Patient() until it
finds a valid (not deleted, not corrupted) patient record.  It
yields that record to the caller, and waits.
When doStuffPart3 has completed, I go back to the top of the for
loop and ask for the next patient.  RecordGenerator steps through
tmpIn, 1024 bytes at a time, until it finds a valid patient, then
yields it and waits.  When it hits the end of the file (or, more
precisely, when the remaining length of tmpIn is less than a full
record length), it quits.

Essentially, you write a generator as if it were a function - but you
use it as if it were a sequence.  Best of both worlds, baby!
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor