
Inline adoc files when they are included exactly once. Also fix language tags because this inlining gives us better information on what language the code is written in.
121 lines
3.0 KiB
Plaintext
121 lines
3.0 KiB
Plaintext
== Why is this an issue?
|
|
|
|
The https://www.python.org/dev/peps/pep-0234/#python-api-specification[iterator protocol] specifies that an iterator object should have
|
|
|
|
* a ``++__next__++`` method retrieving the next value or raising ``++StopIteration++`` when there are no more values left.
|
|
* an ``++__iter__++`` method which should always return ``++self++``. This enables iterators to be used as sequences in for-loops and other places.
|
|
|
|
This rule raises an issue when a class has a ``++__next__++`` method and either:
|
|
|
|
* it doesn't have an ``++__iter__++`` method.
|
|
* or its ``++__iter__++`` method does not return "self".
|
|
|
|
|
|
=== Noncompliant code example
|
|
|
|
[source,python]
|
|
----
|
|
class MyIterator: # Noncompliant. Class has a __next__ method but no __iter__ method
|
|
|
|
def __init__(self, values):
|
|
self._values = values
|
|
self._index = 0
|
|
|
|
def __next__(self):
|
|
if self._index >= len(self._values):
|
|
raise StopIteration()
|
|
value = self._values[self._index]
|
|
self._index += 1
|
|
return value
|
|
|
|
|
|
class MyIterator:
|
|
def __init__(self, values):
|
|
self._values = values
|
|
self._index = 0
|
|
|
|
def __next__(self):
|
|
if self._index >= len(self._values):
|
|
raise StopIteration()
|
|
value = self._values[self._index]
|
|
self._index += 1
|
|
return value
|
|
|
|
def __iter__(self):
|
|
return 42 # Noncompliant. This __iter__ method does not return self
|
|
|
|
|
|
class MyIterable: # Ok. This is an iterable, not an iterator, i.e. it has an __iter__ method but no __next__ method. Thus __iter__ doesn't have to return "self"
|
|
|
|
def __init__(self, values):
|
|
self._values = values
|
|
|
|
def __iter__(self):
|
|
return MyIterator(self._values)
|
|
----
|
|
|
|
|
|
=== Compliant solution
|
|
|
|
[source,python]
|
|
----
|
|
class MyIterator:
|
|
def __init__(self, values):
|
|
self._values = values
|
|
self._index = 0
|
|
|
|
def __next__(self):
|
|
if self._index >= len(self._values):
|
|
raise StopIteration()
|
|
value = self._values[self._index]
|
|
self._index += 1
|
|
return value
|
|
|
|
def __iter__(self):
|
|
return self
|
|
|
|
class MyIterable:
|
|
def __init__(self, values):
|
|
self._values = values
|
|
|
|
def __iter__(self):
|
|
return MyIterator(self._values)
|
|
----
|
|
|
|
|
|
== Resources
|
|
|
|
* https://www.python.org/dev/peps/pep-0234/#python-api-specification[PEP 234 - Iterators]
|
|
* https://docs.python.org/3/library/stdtypes.html#iterator-types[Python documentation - Iterator Types]
|
|
|
|
|
|
ifdef::env-github,rspecator-view[]
|
|
|
|
'''
|
|
== Implementation Specification
|
|
(visible only on this page)
|
|
|
|
=== Message
|
|
|
|
* Add an __iter__ method to this class.
|
|
* Return "self" from this "__iter__" method.
|
|
|
|
|
|
=== Highlighting
|
|
|
|
* If the ++__iter__++ method is present but does not return "self"
|
|
** Primary: The ++__iter__++ method signature
|
|
** Secondary: The returned value if there is one
|
|
* If there is no ++__iter__++ method
|
|
** Primary: The class signature
|
|
** Secondary: The ++__next__++ method signature
|
|
|
|
|
|
'''
|
|
== Comments And Links
|
|
(visible only on this page)
|
|
|
|
=== relates to: S2876
|
|
|
|
endif::env-github,rspecator-view[]
|