This rule raises an issue when a for loop only yields each element of an iterable and could be replaced by a yield from
statement.
Introduced by PEP 380, the yield from statement delegates the iteration to another
iterable.
def generator():
yield from range(10)
Rewriting a for loop whose body is a single yield of the loop variable into yield from has several
benefits:
send(), throw() and close() are routed into it instead of stopping at the
outer generator. Only send() is silently swallowed by a manual loop, but none of the three reach the sub-generator.yield from expression, where a manual loop discards
it.Replace the for loop with a yield from statement over the same iterable.
def read_events(source):
for event in parse_events(source): # Noncompliant: this loop only relays the parsed events
yield event
def read_events(source):
yield from parse_events(source)
yield from is not allowed in asynchronous generators. An async for loop that yields each element cannot be rewritten this
way, and this rule does not raise an issue on such loops.
async def relay(async_iterable):
async for value in async_iterable:
yield value
The two forms are equivalent as long as the sub-iterable is only iterated over. They diverge once the caller drives the outer generator directly:
send(value) is swallowed by a manual loop, which just resumes and yields the next element. yield from passes the value
to the sub-generator.throw(exc) raises the exception in the outer generator, where a manual loop lets it propagate to the caller. yield
from offers it to the sub-generator first, giving it a chance to handle it.close() ends the outer generator in both forms. yield from also closes the sub-generator.The sub-generator’s return value also becomes the value of the yield from expression instead of being discarded. Check how the
sub-iterable is consumed before rewriting a loop that delegates to a generator.
yield
statement