DEV Community

Super Kai (Kazuya Ito)
Super Kai (Kazuya Ito)

Posted on • Edited on

Iterator in Python (4)

Buy Me a Coffee

*Memo:

  • My post explains an iterator (1).
  • My post explains an iterator (2) and the iterator with copy and sorted().
  • My post explains a generator.
  • My post explains the shallow and deep copy of an iterator.

A class-based iterator can be created with __iter__() and/or __next__() as shown below:

*Memo:

  • Basically, __iter__() is used to return an iterator.
  • Basically, __next__() is used to return the next element.

<__iter__() & __next__()>:

class Cls:
    data = [0, 1, 2]
    index = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.index < len(self.data):
            i = self.index
            self.index += 1
            return self.data[i]
        raise StopIteration

v = Cls()
print(v)
# <__main__.Cls object at 0x000001FCD688B350>

# It gets an element but it's not necessary for 
# a `for` statement, `__next__()` and `next()`.
v = v.__iter__()
print(v)
# <__main__.Cls object at 0x000001FCD688B350>

# It gets an element but it's not necessary for 
# a `for` statement, `__next__()` and `next()`.
v = iter(v)
print(v)
# <__main__.Cls object at 0x000001FCD688B350>

for x in v:
    print(x)
# 0
# 1
# 2

v = Cls()

print(v.__next__()) # 0
print(v.__next__()) # 1
print(v.__next__()) # 2
print(v.__next__()) # StopIteration:

v = Cls()

print(next(v)) # 0
print(next(v)) # 1
print(next(v)) # 2
print(next(v)) # StopIteration:
Enter fullscreen mode Exit fullscreen mode

<__next__()>:

class Cls:
    data = [0, 1, 2]
    index = 0

    def __next__(self):
        if self.index < len(self.data):
            i = self.index
            self.index += 1
            return self.data[i]
        raise StopIteration

v = Cls()
print(v)
# <__main__.Cls object at 0x000001FCD8360440>

print(v.__next__()) # 0
print(v.__next__()) # 1
print(v.__next__()) # 2
print(v.__next__()) # StopIteration:

v = Cls()

print(next(v)) # 0
print(next(v)) # 1
print(next(v)) # 2
print(next(v)) # StopIteration:

v = Cls()

v = v.__iter__()
# AttributeError: 'Cls' object has no attribute '__iter__'

v = iter(v)
# TypeError: 'Cls' object is not iterable

for x in v:
    print(x)
# TypeError: 'Cls' object is not iterable
Enter fullscreen mode Exit fullscreen mode

*__next__() can be the generator with one or more yield statement.

class Cls:
    data = [0, 1, 2]
    index = 0

    def __next__(self):
        if self.index < len(self.data):
            i = self.index
            self.index += 1
            yield self.data[i]
            # yield from self.data[i]
        raise StopIteration

v = Cls()
print(v)
# <__main__.Cls object at 0x000001FCD2EE4E60>

print(v.__next__().__next__()) # 0
print(v.__next__().__next__()) # 1
print(v.__next__().__next__()) # 2
print(v.__next__().__next__()) # RuntimeError: generator raised StopIteration

v = Cls()

print(next(next(v))) # 0
print(next(next(v))) # 1
print(next(next(v))) # 2
print(next(next(v))) # RuntimeError: generator raised StopIteration
Enter fullscreen mode Exit fullscreen mode

<__iter__()>:

class Cls:
    data = [0, 1, 2]
    index = 0

    def __iter__(self):
        return self

v = Cls()
print(v)
# <__main__.Cls object at 0x000001FCD6888F80>

v = v.__iter__() # It gets an element but it's not necessary.
print(v)
# <__main__.Cls object at 0x000001FCD6888F80>

v = iter(v)
# TypeError: iter() returned non-iterator of type 'Cls'

for x in v:
    print(x)
# TypeError: iter() returned non-iterator of type 'Cls'

print(v.__next__())
# AttributeError: 'Cls' object has no attribute '__next__'

print(next(v))
# TypeError: 'Cls' object is not an iterator
Enter fullscreen mode Exit fullscreen mode

*__iter__() can be the normal function which returns 'Hello'.

class Cls:
    def __iter__(self):
        return 'Hello'

v = Cls()
print(v)
# <__main__.Cls object at 0x000001FCD2EE4E60>

v = v.__iter__() # It gets an element to print.

print(v)
# Hello

v = Cls()
v = iter(v)
# TypeError: iter() returned non-iterator of type 'list'
Enter fullscreen mode Exit fullscreen mode

*__iter__() can be the generator with one or more yield statement.

class Cls:
    def __iter__(self):
        return iter([0, 1, 2])

    # def __iter__(self): # It's a generator.
    #     yield 0         # It gets the same result.
    #     yield 1
    #     yield 2

    # def __iter__(self):            # It's a generator.
    #     yield from [0, 1, 2] # It gets the same result.

v = Cls()
print(v)
# <__main__.cls object at 0x000001FCD1A14E60>

# It gets an element but it's not necessary for a `for` statement.
v = v.__iter__()
print(v)
# <list_iterator object at 0x000001FCD1C42EF0>

for x in v:
    print(x)

v = Cls()
v = v.__iter__() # It gets an element for `__next__()`.

print(v.__next__()) # A
print(v.__next__()) # B
print(v.__next__()) # C
print(v.__next__()) # StopIteration:

v = Cls()
v = iter(v) # It gets an element for `next()`.

print(next(v)) # A
print(next(v)) # B
print(next(v)) # C
print(next(v)) # StopIteration:
Enter fullscreen mode Exit fullscreen mode

Top comments (0)