Python provides generator functions as a convenient shortcut to building iterators.
# a generator that yields items instead of returning a list def firstn(n): num = 0 while num < n: yield num num += 1 sum_of_first_n = sum(firstn(1000000))
Note that the expression of the number generation logic is clear and natural. It is very similar to the implementation that built a list in memory, but has the memory usage characteristic of the iterator implementation.
built a list:
# Build and return a list def firstn(n): num, nums = 0, [] while num < n: nums.append(num) num += 1 return nums sum_of_first_n = sum(firstn(1000000))
generator class:
# Using the generator pattern (an iterable) class firstn(object): def __init__(self, n): self.n = n self.num = 0 def __iter__(self): return self # Python 3 compatibility def __next__(self): return self.next() def next(self): if self.num < self.n: cur, self.num = self.num, self.num+1 return cur else: raise StopIteration() sum_of_first_n = sum(firstn(1000000))
Note: the above code is perfectly acceptable for expository purposes, but remember that in Python 2 firstn() is equivalent to the built-in xrange() function, and in Python 3 range() is an immutable sequence type. The built-ins will always be much faster. SH
Simply, generator using yield keyword is more simple and clear, you can think this as the compiler built a list in the memory with a iterator for you. It's wirtten as a function without a return statement, but used like list with iterator.
Another exmaple:
# explicitly write a generator function def double(L): for x in L: yield x*2 # eggs will be a generator eggs = double([1, 2, 3, 4, 5]) # the above is equivalent to ("generator comprehension"?) eggs = (x*2 for x in [1, 2, 3, 4, 5]) # need to do this if you need a list eggs = list(double([1, 2, 3, 4, 5])) # the above is equivalent to (list comprehension) eggs = [x*2 for x in [1, 2, 3, 4, 5]]