[Tutor] lazy? vs not lazy? and yielding
spir
denis.spir at gmail.com
Thu Mar 4 10:05:26 CET 2010
On Wed, 03 Mar 2010 15:41:34 -0500
Dave Angel <davea at ieee.org> wrote:
> John wrote:
> > Hi,
> >
> > I just read a few pages of tutorial on list comprehenion and generator
> > expression. From what I gather the difference is "[ ]" and "( )" at the
> > ends, better memory usage and the something the tutorial labeled as "lazy
> > evaluation". So a generator 'yields'. But what is it yielding too?
> >
> > John
> >
> >
> A list comprehension builds a whole list at one time. So if the list
> needed is large enough in size, it'll never finish, and besides, you'll
> run out of memory and crash. A generator expression builds a function
> instead which *acts* like a list, but actually doesn't build the values
> till you ask for them.
To append on Dave's explanation, let us a take special case: when the sequence is potentially infinite. Imagine you want a function (actually here a generator) to yield the sequence of squares of natural without any limit. The stop point will happen in the code using the generator. You could write it eg like that (this is just sample code):
def squares():
squares.n = min if min
while True:
squares.n += 1
n = squares.n
yield n*n
squares.n = 0
Then, you can use it for instance to get the first 9 squares that happen to be multiples of 3.
nineTripleSquares = []
for sq in squares():
if sq%3 == 0:
nineTripleSquares.append(sq)
if len(nineTripleSquares) == 9:
break
print nineTripleSquares
# ==> [9, 36, 81, 144, 225, 324, 441, 576, 729]
A list can hardly be used here, instead of a generator, because we do not know how long the list should be so that we get 9 final items.
We can modify squares to give it borders:
def squares(min=None, max=None):
squares.n = min-1 if (min is not None) else 0
squares.max = max
while True:
squares.n += 1
n = squares.n
if (squares.max is not None) and (n > squares.max):
raise StopIteration
yield n*n
squares.n = 0
tripleSquares = []
for sq in squares(7,27):
if sq%3 == 0:
tripleSquares.append(sq)
print tripleSquares
# [81, 144, 225, 324, 441, 576, 729]
A more object-oriented vaersion would look like:
class Squares(object):
def __init__(self, min, max):
self.n = min-1 if (min is not None) else 0
self.max = max
def next(self):
self.n += 1
n = self.n
if (self.max is not None) and (n > self.max):
raise StopIteration
return n*n
def __iter__(self):
return self
tripleSquares = []
for sq in Squares(7,27):
if sq%3 == 0:
tripleSquares.append(sq)
print tripleSquares
# ==> same result
This defines a new type of "iterable" objects. When you use "for item in obj" on such object, python looks for the magic method __iter__ to find its iterator: here, itself. Then it will repetedly call the iterator's next method to get each item.
Denis
--
________________________________
la vita e estrany
spir.wikidot.com
More information about the Tutor
mailing list