Funkcja iter()

https://docs.python.org/3/library/functions.html#iter

WPROWADZENIE

Funkcja iter(X) zwraca obiekt iteratora, utworzony na bazie obiektu X. Obiekt X musi wspierać protokół iteracji (metoda __iter__, obiekt dostarcza swój iterator) lub protokół sekwencji (metoda __getitem__ z argumentami int startującymi od 0).

Druga wersja wywołania funkcji iter() zwraca iterator zbudowany na bazie obiektu wywoływalnego i strażnika (wartość kończąca iterację).


# Składnia.
# iter(iterable) zwraca iterator
# iter(callable, sentinel) zwraca iterator

# Iterator bazujący na sekwencji.
it1 = iter([1, 2])   # list_iterator object in Py3 (lists have __iter__)
it2 = iter("abcd")   # str_iterator object in Py3 (strings have __iter__)
it3 = iter(it2)   # all iterators are also iterables
assert it3 is it2

# Kiedy mamy iterator, to jedyne co możemy zrobić, to pobierać kolejne elementy
# aż do wyczerpania iteratora, a wtedy pojawi się wyjątek StopIteration.
print(next(it1))   # 1
print(next(it1))   # 2
print(next(it1))   # StopIteration

# Przykład obiektu wspierającego protokół sekwencji.

class Dumb:

    def __init__(self, sequence):
        self.data = sequence

    def __getitem__(self, n):
        return self.data[n]

x = Dumb("word")

#print(dir(x))   # nie ma __iter__, __next__
print(x[1])   # zwraca 'o'
#print(x[10])   # IndexError: string index out of range
#len(x)   # AttributeError: Dumb instance has no attribute '__len__'

for item in x:   # można iterować po x
    print(item)

y = iter(x)         # można jawnie utworzyć iterator

#print(dir(y))   # pojawia się __iter__, __next__ (Py3.7)
print(next(y))   # w
print(next(y))   # o
print(next(y))   # r
print(next(y))   # d
print(next(y))   # StopIteration

# Przygotowanie iteratora na bazie obiektu wywoływalnego.
# Obiekt wywoływalny to zwykle coś więcej niż funkcja,
# bo posiada/utrzymuje swój stan pomiędzy wywołaniami.

class MyInt:
    """Klasa do tworzenia obiektów wywoływalnych, zwracających kolejne int."""

    def __init__(self):
        self.n = 0

    def __call__(self):
        self.n = self.n + 1
        return self.n

x = MyInt()   # x to MyInt instance, obiekt wywoływalny
print(x())   # 1
print(x())   # 2

# Przygotowanie iteratora, który zakończy pracę, jeżeli x zwróci 10.
y = iter(x, 10)     # y to callable-iterator object

for i in y:         # zwróci 3,4,5,6,7,8,9 (bez 10)
    print(i)
# 1 i 2 było wykorzystane wcześniej przez print(),
# 10 wykorzystane na zatrzymanie iteratora
#print(next(y))   # wyzwala StopIteration
print(x())   # 11, obiekt wywoływalny jest aktywny

ITERATORY NIESKOŃCZONE


import itertools

iter_zeros1 = iter(int, 1)   # int() zwraca 0
iter_zeros2 = iter((lambda: 0), 1)   # funkcja bez argumentów to obiekt wywoływalny
iter_zeros3 = itertools.cycle([0])   # itertools.cycle(sequence)
iter_zeros4 = itertools.repeat(0)    # itertools.repeat(x, times=5)

import random

it = iter(MyInt(), 0)   # zwraca 1,2,3,... z obiektu wywoływalnego
it = (random.choice(range(7)) for _ in iter(int, 1))   # przypadkowy dzień tygodnia
it = iter(lambda: random.choice(range(7)), -1)   # jw