Reputation: 858
Let's say I have a class which implements an __iter__()
function, is it preferred to use iter(obj)
or calling obj.__iter__()
directly? Are there any real differences besides having to type 5 characters less with the magic function?
In contrast: For next()
and __next__()
I can see an advantage for having a default value with the magic function.
Upvotes: 7
Views: 3038
Reputation: 1
The iter()
function (which in turn calls the iter() method) is a python built-in function that returns an
iterator from them. You can opt to use iter(obj)
or obj.__iter__()
, they are simply the same thing. Its just
that __iter__()
is a user-defined function and calling it will just do what the written code asks it to do.
On the other hand, calling the iter()
function will in turn call the user-defined __iter__()
function and run
the same code. However, iter()
will also run a built-in 'type' checking, in which if the customized __iter__()
doesn't return an iterator object, it would throw an error.
*The same thing applies to next()
& __next__()
.
Thus, iter(obj) ~= obj.__iter__()
,
next(obj) ~= obj.__next__()
Note that __iter__()
and __next__()
must be defined by the user when creating a class if the object os intended
to be an iterator.
Source: https://www.programiz.com/python-programming/iterator
Upvotes: 0
Reputation: 40713
The difference is mostly just convenience. It's less typing and less symbols to read, and so faster to read. However, the various builtin functions (eg. iter
, len
et al.) usually do a little type checking to catch errors early. If you wrote a customer __iter__
method and it returned 2
, then invoking obj.__iter__()
wouldn't catch that, but iter(obj)
throws a type error. eg.
>>> class X:
def __iter__(self):
return 2
>>> x = X()
>>> x.__iter__()
2
>>> iter(x)
Traceback (most recent call last):
File "<pyshell#37>", line 1, in <module>
iter(x)
TypeError: iter() returned non-iterator of type 'int'
iter
also implements the iterator protocol for objects that have no __iter__
, but do implement the sequence protocol. That is, they have a __getitem__
method which implements a sequence starting at index 0 and raises an IndexError for indexes not in bounds. This is an older feature of python and not really something new code should be using. eg.
>>> class Y:
def __getitem__(self, index):
if 0 <= index < 5:
return index ** 2
else:
raise IndexError(index)
>>> list(iter(Y())) # iter not strictly needed here
[0, 1, 4, 9, 16]
When should you use __iter__
? This might not be so relevant to __iter__
, but if you need access to the implementation of method that the parent class uses then it is best to invoke such methods in the style super().__<dunder_method>__()
(using Python 3 style super usage). eg.
>>> class BizzareList(list):
def __iter__(self):
for item in super().__iter__():
yield item * 10
>>> l = BizzareList(range(5))
>>> l # normal access
[0, 1, 2, 3, 4]
>>> l[0] # also normal access
0
>>> tuple(iter(l)) # iter not strictly needed here
(0, 10, 20, 30, 40)
Upvotes: 12