This rule raises an issue when a non iterable object is used in a for-in loop, in a yield from or when it is
unpacked.
for-in loops, yield
from and iterable unpacking only work with iterable objects. In order
to be iterable, an object should have either an __iter__ method or a __getitem__ method implementing the Sequence protocol.
When trying to iterate over an object which does not implement the required methods, a TypeError will be raised.
Below is an example of a basic implementation of a iterator with __iter__:
class IterExample(object):
def __init__(self):
self._values = [1,2,3,4]
def __iter__(self):
return iter(self._values)
Here is a similar example with __getitem__:
class GetItemExample(object):
def __init__(self):
self._values = [1,2,3,4]
def __getitem__(self, item):
return self._values[item]
These implementations make it possible to execute the following program:
my_iterator = IterExample()
for i in my_iterator:
print(i) # Prints 1,2,3,4
my_iterator = GetItemExample()
for i in my_iterator:
print(i) # Prints 1,2,3,4
Note also that iterating over an asynchronous iterable, i.e. an
object having the __aiter__ method, requires the use of async for ... in instead of for ...
in. Failing to provide the async keyword will result in a TypeError stating the object is not iterable.
Make sure your object is an iterable when using it in for-in loops,yield from and unpacking statements, by implementing
__iter__ or __getitem__. To iterate over an asynchronous iterable, make sure to use the async keyword, i.e
async for … in.
class MyIterable:
def __init__(self, values):
self._values = values
my_iterable = MyIterable(range(10))
for a in my_iterable: # Noncompliant: MyIterable is not an iterable
print(a)
a, b, *c = my_iterable # Noncompliant: MyIterable is not an iterable
# yield from
def generator():
yield from my_iterable # Noncompliant: MyIterable is not an iterable
For async generators:
async def async_function():
# async generators
async def async_generator():
yield 1
for a in async_generator(): # Noncompliant: "async" is missing before "for"
print(a)
class MyIterable:
def __init__(self, values):
self._values = values
def __iter__(self):
return iter(self._values)
my_iterable = MyIterable(range(10))
for a in my_iterable:
print(a)
a, b, *c = my_iterable
# yield from
def generator():
yield from my_iterable
Make sure to use the async keyword when iterating over async generators.
async def async_function():
# async generators
async def async_generator():
yield 1
async for a in async_generator():
print(a)