generator-expression生成器表达式
the differences between list comprehension and generator expression
def gen_AB():
print("start")
yield 'A'
print("continue")
yield 'B'
print("end.")
res1 = [x*3 for x in gen_AB()]
执行结果为:
start
continue
end.
说明列表推导式会析取yield返回的结果,并存储于列表中,其他的便不会存储到列表中。
for i in res1:
print("-->",i)
执行结果为:
–> AAA
–> BBB
但是,生成器表达式却不是这样的。
res2 = (x*3 for x in gen_AB())
这时,输入res2,会返回:
<generator object <genexpr> at 0x10063c20>
for i in res2:
print("-->", i)
执行结果为:
start
–> AAA
continue
–> BBB
end.
**only when the for loop iterates over res2, the body of gen_AB actually executes.
Each iteration of the for loop implicitly calls the next(res2), advancing gen_AB to the next yield.
Note the output of gen_AB with the output of the print in the for loop**
import re
import reprlib
RE_WORD = re.compile("\w+")
class Sentence(object):
def __init__(self, text):
self.text = text
def __repr__(self):
return "Sentence({})".format(reprlib.repr(self.text))
def __iter__(self):
return (match.group() for match in RE_WORD.finditer(self.text))
when to use generator expression?
if the generator expression spans more than a couple of lines, I prefer to code a generator function for the sake of readability.
Also, because generator function have a name, they can be reused.
You can always name a generator expression and use it later by assigning it to a variable, of course, but that is stretching its intended usage as a one-off generator.
Syntax Tip
when a generator expression is passed as the single argument to a function or constructor, you don’t need to
write a set of parentheses for the function call and another to enclose the generator expression.
def __mul__(self, scalar):
if isinstance(scalar, numbers.Real):
return Vector(n*scalar for n in self)# the parentheses is omitted
else:
return NotImplemented
look at the generator expression power!
def gen_Arithmetic(start, step, end=None):
start = type(start+step)(start)
index = 0
result = start
foever = end is None
while foever or result < end:
yield result
index += 1
result = start + index*step
there is a standard library that can be used.
import itertools
gen = itertools.count(1, 0.5)
next(gen)
1
next(gen)
1.5
however, itertools.count never stops.
tertools.takewhile function : it produces a generator that consumer another generator and stop when a given
predicate evaluates to False.
gen = itertools.takewhile(lambda n:n<3, itertools.count(1,0.5))
list(gen)
[1,1.5,2.0,2.5]
def gen_Arithmetic(start, step, end=None):
start = type(start + step)(start)
gen = itertools.count(start,step)
if end is None:
return gen
return itertools.takewhile(lambda v:v<end, gen)
Note that gen_Arithmetic function is not a generator function;
it has no yield in its body.
But it returns a generator, so it operates as a generator factory, just as a generator function does.