-2

I'm a bit confused with the use of methods like __iter__() and __next__() (I suppose they are called dunders). I was trying to understand iterators and iterables and wrote this code:

x = (1, 2, 3, 4, 5, 6)
try:
    y = x.__iter__()
    while 1:
        print(y.__next__())
except StopIteration:
    print('Iterator has exhausted')

Then the code got executes without error when I used __iter__ and __next__ as functions:

x = (1, 2, 3, 4, 5, 6)
try:
    y = iter(x)
    while 1:
        print(next(y))
except StopIteration:
    print('Iterator has exhausted')

Can anybody tell me how they can be used as both a method and function? And is it applicable to all dunders?

newBjava
  • 7
  • 2
  • 5
    [`iter` and `next`](https://docs.python.org/3/library/functions.html) are functions. [`__iter__` and `__next__`](https://docs.python.org/3/library/stdtypes.html#container.__iter__) are methods. – khelwood Aug 02 '20 at 09:35
  • so what you're saying is, __iter__() and __next__() are methods of object tuple (here) and next() and iter() are inbuilt functions. And this thing is not true for other dunders? – newBjava Aug 02 '20 at 09:55
  • "And this thing is not true for other dunders?" What made you infer that? All dunders gave a separate implementation for each class that supports them, whole the associated built-in functions are just implemented once for all types. – Arthur Tacca Aug 02 '20 at 10:04
  • You are not really supposed to actually call magic methods, that's why they may or may not be available for built-in types. However `iter(x)` in the second variant does provide you an iterator, so there you could use `y.__next__()`. – tevemadar Aug 02 '20 at 10:04
  • @tevemadar You contradicted yourself there! Once you've called `y = iter(x)`, you'll want to call `next (y)`, not `y.__next()__`. – Arthur Tacca Aug 02 '20 at 10:06
  • Ah I see what you're saying is not true for all dunders: if you have a method `x.__foo__()` then you can't necessarily call it with `foo()`. That's true, there is a predefined list of built-in functions that call the corresponding dunders. It's longer that just `iter` and `next` though e.g. also `list`, `bool`. Other dunders have other special meanings e.g. `x.__getitem__(y)` is called when you write `x[y]`. – Arthur Tacca Aug 02 '20 at 10:11
  • @ArthurTacca it's simpler: I misunderstood the question and assumed the first code snippet did not work. (However what I wrote works fine too, you can use `y.__next__()` after `y=iter(x)`) – tevemadar Aug 02 '20 at 10:51

1 Answers1

0
  • If you are writing your own class and want it to support being iterated over then write an __iter__ method for it.
  • If you are using a class that supports iteration then call the iter function (or, in most cases where you don't need so much control, just use it in a for loop).

This applied not only to this particular pair but also other dunder methods and associated functions e.g. len() and __len__().

Under the hood, the iter function just calls the __iter__ method of the object passed to it. You might ask: why, then, bother to have an iter function of you can just call the __iter__ method yourself? (Maybe that's what you really wanted to know in the first place.) There are two reasons I can think of:

  • It makes your code look a bit neater if you're calling a function without underscores.
  • Often with these pairs of built-in functions and associated dunder methods, the built-in function actually takes care of a few different details. iter is an example of this: as the docs for it say, it works not only for types with an __iter__ method but also those satisfying the container protocol (i.e. __len__ and __getitem__ methods).
  • (Also, those underscores are meant to indicate that you aren't meant to call the method directly, but that explanation is a bit circular when you're already asking why not call them directly.)
Arthur Tacca
  • 8,833
  • 2
  • 31
  • 49