Reputation: 1399
Can we implement yield
or generator statement (with a loop) within a lambda
?
My question is to clarify:
Whether the following simple loop function can be implemented with yield
def loopyield():
for x in range(0,15):
yield x
print(*loopyield())
Results in error:
lamyield=lambda x: yield x for x in range(0,15)
^
SyntaxError: invalid syntax
Which looks like, it was expecting something as right operand for unwritten return statement but found the yield
and getting confused.
Is there a proper legit way to achieve this in a loop?
Side note: yield
can be statement/expression depending on who you ask: yield - statement or expression?
Final Answer : yield can be used with lambda but the limitation(single-line) makes it useless. for/while
not possible in lambda because they are not expressions. -user2357112 implicit for loop is possible with list comprehension, and yield is valid within the list comprehension. -wim
Verdict- Explicit loops not possible because lambdas in python can only contain expressions, and to write an explicit loop you will need to use statements. -wim
Upvotes: 46
Views: 38200
Reputation: 195
I have a simpler solution
lmbdgen = lambda: (x for x in range(15))
lmbdgen
Out[40]: <function __main__.<lambda>()>
lmbdgen()
Out[41]: <generator object <lambda>.<locals>.<genexpr> at 0x00000171473D8D60>
list(lmbdgen())
Out[42]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
This creates a function that returns a generator. This is a simpler way to access a generator multiple times.
For an alternative version
def defgen_return():
return (x for x in range(10000))
def defgen_yield():
for x in range(10000):
yield x
Here is the performance difference
%timeit list(defgen_return())
613 µs ± 9.13 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
%timeit list(defgen_yield())
572 µs ± 20.5 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
%timeit list(lmbdgen())
581 µs ± 22.9 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
Upvotes: 3
Reputation: 363183
The one-liner you seem to be trying to create is actually technically possible with a lambda, you just need to help the parser a bit more:
>>> lamyield = lambda: [(yield x) for x in range(15)]
>>> print(*lamyield())
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
This uses a for loop implicitly in a list comprehension. It is not possible with an explicit while
loop or for
loop outside of a comprehension. That's because lambdas in Python can only contain expressions, and to write an explicit loop you will need to use statements.
Note: this syntax is deprecated in Python 3.7, and will raise SyntaxError
in Python 3.8
Upvotes: 50
Reputation: 45291
You actually can loop through a lambda in useful ways, it's just that the example you provided isn't a great use case.
One instance where you might want to use yield
inside a lambda
might be to lazily execute expensive functions only when needed. Like so:
def expensive_check1():
print("expensive_check1")
return True
def expensive_check2():
print("expensive_check2")
return True
def expensive_check3():
print("expensive_check3")
return True
def do_the_thing(*args):
print(args)
if __name__=="__main__":
for check, args in (lambda: (
(yield (expensive_check1(), ["foo", "bar"])),
(yield (expensive_check2(), ["baz"])),
(yield (expensive_check3(), [])),
))():
if check:
do_the_thing(*args)
continue
raise Exception("oh noes!!!")
else:
print("all OK!")
Output:
expensive_check1
('foo', 'bar')
expensive_check2
('baz',)
expensive_check3
()
all OK!
Note that the expensive checks only happen at the start of each loop, rather than all at once. Also note that this syntax will still work in Python 3.8+, since it is not using the yield
inside of a comprehension.
Upvotes: 6
Reputation: 1624
Is it any necessity for using yield
inside of lambda
if you can rewrite it with generator such that?
In[1]: x = (i for i in range(15))
In[2]: x
Out[2]: <generator object <genexpr> at 0x7fbdc69c3f10>
In[3]: print(*x)
Out[3]: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
In[4]: x = (i for i in range(0, 15))
In[5]: x.__next__()
Out[5]: 0
In[6]: next(x)
Out[6]: 1
Upvotes: 20