class test(object):
def __init__(self):
pass
def __iter__(self):
return \"my string\"
o = test()
print iter(o)
Why does th
The code can be repaired by adding an iter() call:
class test(object):
def __init__(self):
pass
def __iter__(self):
return iter("my string")
Here is a sample run:
>>> o = test()
>>> iter(o)
<iterator object at 0x106bfa490>
>>> list(o)
['m', 'y', ' ', 's', 't', 'r', 'i', 'n', 'g']
The reason for the original error is that the API for __iter__ purports to return an actual iterator. The iter() function checks to make sure the contract is fulfilled.
Note, this kind of error checking occurs in other places as well. For example, the len() function checks to make sure a __len__() method returns an integer:
>>> class A:
def __len__(self):
return 'hello'
>>> len(A())
Traceback (most recent call last):
File "<pyshell#4>", line 1, in <module>
len(A())
TypeError: 'str' object cannot be interpreted as an integer
To answer your specific question. Python2 appears to check for the presence of a .next
class attribute:
>>> class test(object):
... next = None
... def __iter__(self):
... return self
...
>>> print iter(test())
<__main__.test object at 0x7fcef75c2f50>
An instance attribute won't do:
>>> class test(object):
... def __init__(self):
... self.next = None
... def __iter__(self):
... return self
...
>>> print iter(test())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: iter() returned non-iterator of type 'test'
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
The purpose of __iter__
magic function is to return something that you can iterate (e.g. loop) through. The most common solution is to return iter(something)
where something
could be a list, a tuple, set, dictionary, a string... anything that we can iterate through. Take a look at this example:
class Band:
def __init__(self):
self.members = []
def add_member(self, name):
self.members.append(name)
def __iter__(self):
return iter(self.members)
if __name__ == '__main__':
band = Band()
band.add_member('Peter')
band.add_member('Paul')
band.add_member('Mary')
# Magic of __iter__:
for member in band:
print(member)
Output:
Peter
Paul
Mary
In this case, the __iter__
magic function allows us to loop through band
as if it is a collection of members. That means in your case, return "my string" will not do. If you want a list of chars in "my string":
def __iter__(self):
return iter("my string") # ==> m, y, ' ', s, t, r, i, n, g
However, if you want to return a list with a single element "my string", then:
def __iter__(self):
return iter(["my string"])