The following code creates two
list
s,
oldList
and
newList
.
oldList = [10, 110, 50, 120, 30] newList = [item for item in oldList if item >= 100] #list comprehension print(f"type(newList) = {type(newList)}") for item in newList: print(item)
type(newList) = <class 'list'> 110 120
The following code produces the same output
but creates only one
list
.
oldList = [10, 110, 50, 120, 30] it = (item for item in oldList if item >= 100) #generator expression print(f"type(it) = {type(it)}") for item in it: print(item)
type(it) = <class 'generator'> 110 120
The above code does the same thing as
def f(oldList): #generator function for item in oldList: if item >= 100: yield item oldList = [10, 110, 50, 120, 30] it = f(oldList) print(f"type(it) = {type(it)}") for item in it: print(item)
type(it) = <class 'generator'> 110 120
As we have seen, a
generator
function
is often just a
yield
statement inside a
for
loop,
or maybe just a
yield
statement inside an
if
statement inside a
for
loop.
In this case, a
generator
expression
is a convenient abbreviation for the
generator
function.
If you want only the first number that is ≥ 100,
call
next
only once.
oldList = [10, 110, 50, 120, 30] it = (item for item in oldList if item >= 100) try: i = next(it) #means i = it.__next__() print(f"The first number greater >= 100 is {i}.") except StopIteration: print("All the numbers in the oldList are < 100.")
The first number greater than or equal to 100 is 110.
More compact way of doing the same thing:
oldList = [10, 110, 50, 120, 30] try: i = next(item for item in oldList if item >= 100) print(f"The first number greater >= 100 is {i}.") except StopIteration: print("All the numbers in the oldList are < 100.")
The first number greater than or equal to 100 is 110.
For this job, a list comprehension is worse than a generator for three reasons:
oldList = [10, 110, 50, 120, 30] newList = [item for item in oldList if item >= 100] try: i = newList[0] print(f"The first number >= 100 is {i}.") except IndexError: print("All the numbers in the oldList are < 100.")
The first number >= 100 is 110.
Old fashioned way:
oldList = [10, 110, 50, 120, 30] for i in oldList: if i >= 100: print(f"The first number >= 100 is {i}.") break else: #Arrive here if the break statement was never executed. print("All the numbers in the oldList are < 100.")
The first number >= 100 is 110.
Create the iterator manually:
class FloatRange(object): "A range of n+1 equally spaced floats." def __init__(self, start, end, n): self.start = start self.end = end self.n = n def __iter__(self): return FloatRange_iterator(self.start, self.end, self.n) class FloatRange_iterator(object): def __init__(self, start, end, n): self.start = start self.end = end self.n = n self.i = 0 def __iter__(self): return self def __next__(self): if self.i >= self.n + 1: raise StopIteration result = self.start + (self.end - self.start) * self.i / self.n self.i += 1 return result for f in FloatRange(0.0, 1.0, 10): print(f)
0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0
With a generator function:
def FloatRange(start, end, n): for i in range(n + 1): yield start + (end - start) * i / n for f in FloatRange(0.0, 1.0, 10): print(f)
0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0
With a generator expression:
def FloatRange(start, end, n): return (start + (end - start) * i / n for i in range(n + 1)) for f in FloatRange(0.0, 1.0, 10): print(f)
0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0
li
is a
list
containing 11
float
s.
it
is an iterator whose
__next__
method will return a different
float
each time you call it.
But on the twelfth call, it will raise a
StopIteration
exception.
start = 0.0 end = 1.0 n = 10 li = [start + (end - start) * i / n for i in range(n + 1)] it = (start + (end - start) * i / n for i in range(n + 1)) for f in li: #or for f in it: print(f)
0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0