str
is an iterable but not an iterator, subtle but important difference. See this answer for an explanation.
You want to return an object with __next__
(or just next
if py2) which is what str
returns when is iterated.
def __iter__(self):
return iter("my string")
str
does not implement __next__
In [139]: s = 'mystring'
In [140]: next(s)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-140-bc0566bea448> in <module>()
----> 1 next(s)
TypeError: 'str' object is not an iterator
However calling iter returns the iterator, which is what looping calls:
In [141]: next(iter(s))
Out[141]: 'm'
You get the same problem returning anything without a __next__
(or next
in py2) method
You can use a generator, which itself has __iter__
that returns self
:
def gen():
yield 'foo'
gg = gen()
gg is gg.__iter__()
True
gg.__next__()
'foo'
class Something:
def __iter__(self):
return gen()
list(Something())
['foo']
Or a class where you implement __next__
yourself, like this class similar to the one on the Ops post (you also have to handle StopIteration
which stops the loop)
class test:
def __init__(self, somestring):
self.s = iter(somestring)
def __iter__(self):
return self
def __next__(self):
return next(self.s) ## this exhausts the generator and raises StopIteration when done.
In [3]: s = test('foo')
In [4]: for i in s:
...: print(i)
...:
f
o
o