Reputation: 143
What is the difference between iter(x)
and x.__iter__()
?
From my understanding, they both return a listiterator
object but, in the below example, I notice that they are not equal:
x = [1, 2, 3]
y = iter(x)
z = x.__iter__()
y == z
False
Is there something that I am not understanding about iterator objects?
Upvotes: 7
Views: 1389
Reputation: 3650
Iter objects dont have equality based on this sort of thing.
See that iter(x) == iter(x)
returns False
as well. This is because the iter function (which calls __iter__
) returns an iter object that doesnt overload __eq__
and therefore only returns True
when the 2 objects are the same.
Without overloading, ==
is the same as the is
comparison.
Also, x.__iter__().__class__ is iter(x).__class__
showing that, in this case, they return the same type of object.
Upvotes: 9
Reputation: 851
The is no difference between making an iterator through calling iter(x)
or x.__iter__()
. You are making 2 listiterator
objects and comparing them. They are compared to see if they are the same iterator object not what they produce.
>>> test = [1,2,3,4]
>>> iter(test)
<listiterator object at 0x7f85c7efa9d0>
>>> test.__iter__()
<listiterator object at 0x7f85c7efaa50>
You can see that 2 different objects are produced.
It's the same if you call iter(test)
twice too.
You can make them the same by pointing a two variables at the same object.
>>> test = [1,2,3,4]
>>> iter_one = iter(test)
>>> iter_two = iter_one
>>> print iter_one == iter_two
True
>>> iter_one.next()
1
>>> iter_two.next()
2
You can see they both reference the same object.
You can check if difference iterators produce the same output by converting them back to lists again.
>>> print list(iter(test)) == list(test.__iter__())
True
Upvotes: 1
Reputation: 250971
A list supports multiple iterators in Python, so calling iter()
on it each time will return a new iterator object and they don't have their own __eq__
method, hence in the end Python ends up comparing them using their IDs, which is different of course.
>>> type(iter([])).__eq__
<method-wrapper '__eq__' of type object at 0x100646ea0>
>>> object.__eq__
<method-wrapper '__eq__' of type object at 0x100650830>
A class whose instances support only one iterator:
class A(object):
def __iter__(self):
return self
def next(self):
pass
a = A()
y = iter(a)
z = a.__iter__()
print y == z # will print True
It is possible to get True
for different iterators as well but only if you return an iterator type that compare equal on comparison.
With that said never call iter(obj)
like obj.__iter__()
, because in this case instead of looking up for the __iter__
on the class it will look for __iter__
on the instance first:
class A(object):
def __init__(self):
self.__iter__ = lambda: iter(())
def __iter__(self):
return iter([])
a = A()
print a.__iter__()
print iter(a)
# output
<tupleiterator object at 0x10842b250>
<listiterator object at 0x10842b2d0>
Upvotes: 1
Reputation: 90909
They are not completely the same always. From documentation -
iter(o[, sentinel])
Return an iterator object. The first argument is interpreted very differently depending on the presence of the second argument. Without a second argument, o must be a collection object which supports the iteration protocol (the
__iter__()
method), or it must support the sequence protocol (the__getitem__()
method with integer arguments starting at 0).
But for your list case, they are similar, as in iter()
internally calls __iter__()
. But they both return different iterator objects, you can iterate both returned value for both separately, and hence they are not equal.
Example to show general case of iterators -
In [13]: class CA:
....: def __iter__(self):
....: print('Inside __iter__')
....: return iter([1,2,3,4])
....:
In [14]: c = CA()
In [15]: iter(c)
Inside __iter__
Out[15]: <list_iterator at 0x3a13d68>
In [16]: c.__iter__()
Inside __iter__
Out[16]: <list_iterator at 0x3a13908> #see that even the ids are different for the list_iterator objects.
In [17]: class BA:
....: def __getitem__(self,i):
....: print('Inside __getitem__')
....: return i+5
....:
In [18]: b = BA()
In [19]: iter(b)
Out[19]: <iterator at 0x3a351d0>
In [20]: x = iter(b)
In [21]: next(x)
Inside __getitem__
Out[21]: 5
In [23]: next(x)
Inside __getitem__
Out[23]: 6
Example to show that each call to iter()
returns a different iterator object , that can be iterated upon separately -
In [24]: i = iter(c)
Inside __iter__
In [25]: j = iter(c)
Inside __iter__
In [26]: for x in i:
....: pass
....:
In [27]: next(i)
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-27-bed2471d02c1> in <module>()
----> 1 next(i)
StopIteration:
In [28]: next(j)
Out[28]: 1
As you can see above, even though i
got exhausted, j
was still at the starting position, so you can see both are completely different objects (with different states) .
Upvotes: 1
Reputation: 8376
Whenever you call iter(x) you will get a new instance of an Iterator which will point to the same object. When you do a = iter(x)
and b = iter(x)
you have two iterators which point to one object. As the Iterator does not define __eq__()
as equality of the object pointed to, but instead the iterator instance, even iter(x) == iter(x)
will return false.
iter() basically calls the __iter__() function of the object:
Without a second argument, o must be a collection object which supports the iteration protocol (the __iter__() method), or it must support the sequence protocol
Upvotes: 0