Разное

Iterable python: Про Python — Справочник — Iterable (итерируемый)

Содержание

Понимание итераторов в Python / Хабр

Python — особенный язык в плане итераций и их реализации, в этой статье мы подробно разберём устройство итерируемых объектов и пресловутого цикла for.

Особенности, с которыми вы часто можете столкнуться в повседневной деятельности

1. Использование генератора дважды

>>> numbers = [1,2,3,4,5]

>>> squared_numbers = (number**2 for number in numbers)

>>> list(squared_numbers)
[1, 4, 9, 16, 25]

>>> list(squared_numbers)
[]

Как мы видим в этом примере, использование переменной squared_numbers дважды, дало ожидаемый результат в первом случае, и, для людей незнакомых с Python в достаточной мере, неожиданный результат во втором.

2. Проверка вхождения элемента в генератор

Возьмём всё те же переменные:

>>> numbers = [1,2,3,4,5]
>>> squared_numbers = (number**2 for number in numbers)

А теперь, дважды проверим, входит ли элемент в последовательность:

>>> 4 in squared_numbers
True
>>> 4 in squared_numbers
False

Получившийся результат также может ввести в заблуждение некоторых программистов и привести к ошибкам в коде.

3. Распаковка словаря

Для примера используем простой словарь с двумя элементами:

>>> fruits_amount = {'apples': 2, 'bananas': 5}

Распаковываем его:

>>> x, y = fruits_amount

Результат будет также неочевиден, для людей, не понимающих устройство Python, «под капотом»:

>>> x
'apples'
>>> y
'bananas'

Последовательности и итерируемые объекты

По-сути, вся разница, между последовательностями и итерируемымыи объектами, заключается в том, что в последовательностях элементы упорядочены.

Так, последовательностями являются: списки, кортежи и даже строки.

>>> numbers = [1,2,3,4,5]
>>> letters = ('a','b','c')
>>> characters = 'habristhebestsiteever'
>>> numbers[1]
2
>>> letters[2]
'c'
>>> characters[11]
's'
>>> characters[0:4]
'habr'

Итерируемые объекты же, напротив, не упорядочены, но, тем не менее, могут быть использованы там, где требуется итерация: цикл for, генераторные выражения, списковые включения — как примеры.

# Can't be indexed
>>> unordered_numbers = {1,2,3}
>>> unordered_numbers[1]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'set' object is not subscriptable

>>> users = {'males': 23, 'females': 32}
>>> users[1]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 1

# Can be used as sequence
>>> [number**2 for number in unordered_numbers]
[1, 4, 9]
>>>
>>> for user in users:
...     print(user)
... 
males
females

Отличия цикла for в Python от других языков

Стоит отдельно остановиться на том, что цикл for, в Python, устроен несколько иначе, чем в большинстве других языков. Он больше похож на for...each, или же for...of.

Если же, мы перепишем цикл for с помощью цикла while, используя индексы, то работать такой подход будет только с последовательностями:

>>> list_of_numbers = [1,2,3]
>>> index = 0
>>> while index < len(list_of_numbers):
...     print(list_of_numbers[index])
...     index += 1
... 
1
2
3

А с итерируемыми объектами, последовательностями не являющимися, не будет:

>>> set_of_numbers = {1,2,3}
>>> index = 0 
>>> while index < len(set_of_numbers):
...     print(set_of_numbers[index])
...     index += 1
... 
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
TypeError: 'set' object is not subscriptable

Если же вам нужен index, то следует использовать встроенную функцию enumerate:

>>> set_of_numbers = {1,2,3}
>>> for index, number in enumerate(set_of_numbers):
...     print(number, index)
... 
1 0
2 1
3 2

Цикл for использует итераторы

Как мы могли убедиться, цикл for не использует индексы. Вместо этого он использует так называемые итераторы.

Итераторы — это такие штуки, которые, очевидно, можно итерировать 🙂

Получить итератор мы можем из любого итерируемого объекта.

Для этого нужно передать итерируемый объект во встроенную функцию iter:

>>> set_of_numbers = {1,2,3}
>>> list_of_numbers = [1,2,3]
>>> string_of_numbers = '123'
>>> 
>>> iter(set_of_numbers)
<set_iterator object at 0x7fb192fa0480>
>>> iter(list_of_numbers)
<list_iterator object at 0x7fb193030780>
>>> iter(string_of_numbers)
<str_iterator object at 0x7fb19303d320>

После того, как мы получили итератор, мы можем передать его встроенной функции next.

>>> set_of_numbers = {1,2,3}
>>> 
>>> numbers_iterator = iter(set_of_numbers)
>>> next(numbers_iterator)
1
>>> next(numbers_iterator)
2

При каждом новом вызове, функция отдаёт один элемент. Если же в итераторе элементов больше не осталось, то функция next породит исключение StopIteration.

>>> next(numbers_iterator)
3
>>> next(numbers_iterator)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

По-сути, это единственное, что мы может сделать с итератором: передать его функции next.

Как только итератор становится пустым и порождается исключение StopIteration, он становится совершенно бесполезным.

Реализация цикла for с помощью функции и цикла while

Используя полученные знания, мы можем написать цикл for, не пользуясь самим циклом for. 🙂

Чтобы сделать это, нам нужно:

  1. Получить итератор из итерируемого объекта.
  2. Вызвать функцию next.
  3. Выполнить ‘тело цикла’.
  4. Закончить цикл, когда будет получено исключение StopIteration.
def for_loop(iterable, loop_body_func):
    iterator = iter(iterable)
    next_element_exist = True
    while next_element_exist:
        try:
            element_from_iterator = next(iterator)
        except StopIteration:
            next_element_exist = False
        else:
            loop_body_func(element_from_iterator)

Стоит заметить, что здесь мы использовали конструкцию try-else. Многие о ней не знают. Она позволяет выполнять код, если исключения не возникло, и код был выполнен успешно.

Теперь мы знакомы с протоколом итератора.

А, говоря простым языком — с тем, как работает итерация в Python.

Функции iter и next этот протокол формализуют. Механизм везде один и тот же. Будь то пресловутый цикл for или генераторное выражение. Даже распаковка и «звёздочка» используют протокол итератора:

coordinates = [1,2,3]
x, y, z = coordinates

numbers = [1,2,3,4,5]
a,b, *rest = numbers

print(*numbers)

Генераторы — это тоже итераторы

Генераторы тоже реализуют протокол итератора:

>>> def custom_range(number):
...     index = 0 
...     while index < number:
...             yield index
...             index += 1
... 
>>> range_of_four = custom_range(4)
>>> next(range_of_four)
0
>>> next(range_of_four)
1
>>> next(range_of_four)
2
>>> next(range_of_four)
3
>>> next(range_of_four)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

В случае, если мы передаём в iter итератор, то получаем тот же самый итератор

>>> numbers = [1,2,3,4,5]
>>> iter1 = iter(numbers)
>>> iter2 = iter(iter1)
>>> next(iter1)
1
>>> next(iter2)
2
>>> iter1 is iter2
True

Подытожим.

Итерируемый объект — это что-то, что можно итерировать.
Итератор — это сущность порождаемая функцией iter, с помощью которой происходит итерирование итерируемого объекта.

Итератор не имеет индексов и может быть использован только один раз.

Протокол итератора

Теперь формализуем протокол итератора целиком:

  1. Чтобы получить итератор мы должны передать функции iter итерируемый объект.
  2. Далее мы передаём итератор функции next.
  3. Когда элементы в итераторе закончились, порождается исключение StopIteration.

Особенности:

  1. Любой объект, передаваемый функции iter без исключения TypeError — итерируемый объект.
  2. Любой объект, передаваемый функции next без исключения TypeError — итератор.
  3. Любой объект, передаваемый функции iter и возвращающий сам себя — итератор.

Плюсы итераторов:

  1. Итераторы работают «лениво» (en. lazy). А это значит, что они не выполняют какой-либо работы, до тех пор, пока мы их об этом не попросим.

  2. Таким образом, мы можем оптимизировать потребление ресурсов ОЗУ и CPU, а так же создавать бесконечные последовательности.

Итераторы повсюду

Мы уже видели много итераторов в Python.

Я уже упоминал о том, что генераторы — это тоже итераторы.

Многие встроенные функции является итераторами.

Так, например, enumerate:

>>> numbers = [1,2,3]
>>> enumerate_var = enumerate(numbers)
>>> enumerate_var
<enumerate object at 0x7ff975dfdd80>
>>> next(enumerate_var)
(0, 1)

А так же zip:

>>> letters = ['a','b','c']
>>> z = zip(letters, numbers)
>>> z
<zip object at 0x7ff975e00588>
>>> next(z)
('a', 1)

И даже open:

>>> f = open('foo.txt')
>>> next(f)
'bar\n'
>>> next(f)
'baz\n'
>>> 

В Python очень много итераторов, и, как уже упоминалось выше, они откладывают выполнение работы до того момента, как мы запрашиваем следующий элемент с помощью next. Так называемое, «ленивое» выполнение.

Создание собственного итератора

Так же, в некоторых случаях, может пригодится знание того, как написать свой собственный итератор и ленивый итерируемый объект.

В моей карьере этот пункт был ключевым, так как вопрос был задан на собеседовании, которое, как вы могли догадаться, я успешно прошёл и получил свою первую работу:)

class InfiniteSquaring:
"""Класс обеспечивает бесконечное последовательное возведение в квадрат заданного числа."""
    def __init__(self, initial_number):
        # Здесь хранится промежуточное значение
        self.number_to_square = initial_number

    def __next__(self):
        # Здесь мы обновляем значение и возвращаем результат
        self.number_to_square = self.number_to_square ** 2
        return self.number_to_square

    def __iter__(self):
        """Этот метод позволяет при передаче объекта функции iter возвращать самого себя, тем самым в точности реализуя протокол итератора."""
        return self
>>> squaring_of_six = InfiniteSquaring(6)
>>> next(squaring_of_six)
36
>>> next(squaring_of_six)
1296
>>> next(squaring_of_six)
1679616
>>> next(squaring_of_six)
2821109907456
>>> next(squaring_of_six)
7958661109946400884391936
>>> # И так до бесконечности...

Так же:

>>>iter(squaring_of_six) is squaring_of_six
True

Таким образом мы написали бесконечный и ленивый итератор.

А это значит, что ресурсы он будет потреблять только при вызове.

Не говоря уже о том, что без собственного итератора имлементация бесконечной последовательности была бы невозможна.

А теперь вернёмся к тем особенностям, которые были изложены в начале статьи

1. Использование генератора дважды

>>> numbers = [1,2,3,4,5]

>>> squared_numbers = (number**2 for number in numbers)

>>> list(squared_numbers)
[1, 4, 9, 16, 25]

>>> list(squared_numbers)
[]

В данном примере, список будет содержать элементы только в первом случае, потому что генераторное выражение — это итератор, а итераторы, как мы уже знаем — сущности одноразовые. И при повторном использовании не будут отдавать никаких элементов.

2. Проверка вхождения элемента в генератор

>>> numbers = [1,2,3,4,5]
>>> squared_numbers = (number**2 for number in numbers)

А теперь дважды проверим, входит ли элемент в последовательность:

>>> 4 in squared_numbers
True
>>> 4 in squared_numbers
False

В данном примере, элемент будет входить в последовательность только 1 раз, по причине того, что проверка на вхождение проверяется путем перебора всех элементов последовательности последовательно, и как только элемент обнаружен, поиск прекращается. Для наглядности приведу пример:

>>> 4 in squared_numbers
True
>>> list(squared_numbers)
[9, 16, 25]
>>> list(squared_numbers)
[]

Как мы видим, при создании списка из генераторного выражения, в нём оказываются все элементы, после искомого. При повторном же создании, вполне ожидаемо, список оказывается пуст.

3. Распаковка словаря

При использовании в цикле for, словарь будет отдавать ключи:

>>> fruits_amount = {'apples': 2, 'bananas': 5}

>>> for fruit_name in fruits_amount:
...     print(fruit_name)
... 
apples
bananas

Так как распаковка опирается на тот же протокол итератора, то и в переменных оказываются именно ключи:

>>> x, y = fruits_amount
>>> x
'apples'
>>> y
'bananas'

Выводы

Последовательности — итерируемые объекты, но не все итерируемые объекты — последовательности.

Итераторы — самая простая форма итерируемых объектов в Python.

Любой итерируемый объект реализует протокол итератора. Понимание этого протокола — ключ к пониманию любых итераций в Python.

Итераторы в Python — Pythonist

1. Итератор языка Python

В этой статье, посвященной итераторам и их применению в языке Python, мы также рассмотрим создание своих собственных методов __iter__() и __next__(), соберем свой собственный итератор, рассмотрим всю пользу итераторов и закрепим наши знания на примерах.

Итак, начинаем наш туториал по итераторам в Python.

2. Кто такие эти ваши итераторы?

Итератор в языке программирования Python — это объект, который вы можете перебирать. То есть он возвращает по одному объекту за раз. Итератор Python, неявно реализован в таких конструкциях, как циклы for, comprehension’ах и генераторах Python. Функции iter() и next() как раз и образуют протокол итератора.

Если мы можем получить итератор от объекта в Python, значит он итерабельный. Например, списки Python, кортежи и строки.

3. А как создать итератор?

Чтобы собрать итератор python3, мы используем функции iter() и next(). Давайте начнем с iter(), чтобы создать итератор.

Сначала мы создаем список, который содержит все четные числа от 2 до 10.

evens=[2,4,6,8,10]

Затем мы применяем функцию iter() к этому списку Python, чтобы создать объект итератора. Мы храним его в переменной evenIterator.

evenIterator=iter(evens)
evenIterator
<list_iterator object at 0x05E35410>

Помните, итератор можно получить не только для списка, кортежи и даже множества отлично подойдут.

iter((1,3,2))
<tuple_iterator object at 0x05E35550>

Теперь, чтобы получить доступ к первому элементу, мы применяем функцию next() к объекту итератора Python.

next(evenIterator)
2
next(evenIterator)
4
next(evenIterator)
6
next(evenIterator)
8
next(evenIterator)
10

Мы достигли конца списка. Когда мы вызываем его еще раз, мы провоцируем ошибку StopIteration (исключение). Интерпретатор сразу же выбрасывает его.

next(evenIterator)
Traceback (most recent call last):
File “<pyshell#442>”, line 1, in <module>
next(evenIterator)
StopIteration

С методом iter() разобрались, посмотрим на __next__()

Итак, вы можете пройтись по итератору в Python, используя метод __next __() вместо next(). (Ну, мало ли…)

nums=[1,2,3]
numIter=iter(nums)
numIter.__next__()
1
>>> next(numIter)
2
>>> numIter.__next__()
3
>>> numIter.__next__()
Traceback (most recent call last):
File “<pyshell#448>”, line 1, in <module>
numIter.__next__()
StopIteration

Мы можем увидеть этот метод с помощью функции dir().

>>> dir(numIter)
[‘__class__’, ‘__delattr__’, ‘__dir__’, ‘__doc__’, ‘__eq__’, ‘__format__’, ‘__ge__’, ‘__getattribute__’, ‘__gt__’, ‘__hash__’, ‘__init__’, ‘__init_subclass__’, ‘__iter__’, ‘__le__’, ‘__length_hint__’, ‘__lt__’, ‘__ne__’, ‘__new__’, ‘__next__’, ‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__setattr__’, ‘__setstate__’, ‘__sizeof__’, ‘__str__’, ‘__subclasshook__’]

4. Цикл for для итераторов Python

Вы также можете использовать цикл for в Python для итерации по какому-нибудь итерируемому объекту, например, по списку Python или по кортежу.

for i in 'Python':
    print(i)
P
y
t
h
o
n

Но как это на самом деле реализовано? Давайте взглянем.

iter_obj=iter('Python')
while True:
    try:
        i=next(iter_obj)
        print(i)
    except StopIteration
         break
P
y
t
h
o
n

Это была простая версия. Вот как на самом деле реализован вышеприведенный цикл for.

5. Наконец создаем свой собственный итератор

Теперь вы знаете, как использовать итератор с функциями iter() и next(). Но мы не остановимся на этом. Теперь мы начнем с самого нуля.

Мы реализуем следующий класс для создания итератора в Python для квадратов чисел от 1 до максимального указанного.

class PowTwo:

    def __init__(self, max=0):
        self.max = max

    def __iter__(self):
        self.n = 0
        return self

    def __next__(self):
        if self.n <= self.max:
            result = 2 ** self.n
            self.n += 1
            return result
        else:
            raise StopIteration

Здесь __init __() принимает значение max. Затем мы создаем объект «a» класса PowTwo с аргументом 4. Затем мы создаем итератор, используя iter(). Далее мы используем функцию next(), чтобы получать элементы один за другим.

a=PowTwo(4)
i=iter(a)
next(i)
1
>>> next(i)
2
>>> next(i)
4
>>> next(i)
8
>>> next(i)
16
>>> next(i)
Traceback (most recent call last):
File “<pyshell#484>”, line 1, in <module>
next(i)
File “<pyshell#476>”, line 13, in __next__
raise StopIteration
StopIteration

В качестве альтернативы вы можете использовать методы __iter __() и __next __() для этого объекта.

j=a.__iter__()
j.__next__()
1
>>> j.__next__()
2
>>> j.__next__()
4
>>> j.__next__()
8
>>> j.__next__()
16
>>> j.__next__()
Traceback (most recent call last):
File “<pyshell#491>”, line 1, in <module>
j.__next__()
File “<pyshell#476>”, line 13, in __next__
raise StopIteration
StopIteration

Функция iter() вызывает метод __iter __() внутри себя.

6. Бесконечный итератор

В Python действительно возможно создать итератор, который никогда не исчерпывается. Функция iter() может принимать другой аргумент, называемый «страж». Этот страж является точкой выхода и работает следующим образом: как только значение, возвращаемое итератором равно значению стража, итератор заканчивается.

Мы знаем, что функция int() без параметра внутри возвращает 0.

>>> int()

Теперь мы вызываем iter() с двумя аргументами — int и 1.

>>> a=iter(int,1)

Этот итератор Python никогда не исчерпает себя, он бесконечен. Это потому, что 0 никогда не равен 1. Серьезно, никогда.

>>> next(a)
>>> next(a)
>>> next(a)
>>> next(a)
>>> next(a)

И так далее.

Чтобы создать бесконечный итератор Python с использованием класса, рассмотрим следующий пример.

class Even:
        def __iter__(self):
            self.num=2
            return self
        def __next__(self):
            num=self.num
            self.num+=2
            return num             
>>> e=Even()
>>> i=iter(e)
>>> next(i)
2
>>> next(i)
4
>>> next(i)
6
>>> next(i)
8

Здесь Python перебирает четные числа, начинающиеся с 2 и никогда не заканчивающиеся. Таким образом, вы должны быть осторожны и обеспечить завершающее условие (точку выхода).

7. Преимущества итераторов языка Python

Итератор в python экономит ресурсы. Чтобы получить все элементы, в памяти одновременно хранится только один элемент. В отличие от итератора, список должен хранить все значения одновременно.

8. Делаем выводы

В этой статье мы узнали об итераторах Python. Разве они не веселые и супер удобные? Итератор использует всего две функции — iter() и next(). Тем не менее, мы можем сделать наш собственный итератор в Python при помощи класса. Наконец, мы рассмотрели также бесконечные итераторы.

Кроме того, если у вас есть какие-либо вопросы/сомнения, не стесняйтесь задавать их в поле для комментариев.

Как создать свой итератор в Python

Некоторое время назад я написал статью о протоколе итераторов, который поддерживает цикл for в Python. Одна вещь, которую я пропустил в этой статье, это то, как создавать свои собственные итераторы.

В этой статье я собираюсь рассказать, зачем создавать свои собственные итераторы, а затем покажу, как это сделать.

Что такое итератор?

Сначала давайте быстро рассмотрим, что такое итератор. Для более подробного объяснения посмотрите мой доклад Loop Better или прочитайте статью на основе этого доклада.

Итерируемые объекты (iterable) — это любые объекты, предоставляющий возможность поочерёдного прохода по циклу.

Итератор (iterator) — это объект, который выполняет фактический проход по элементам.

Вы можете получить итератор из любых итерируемых объектов, вызвав встроенную функцию iter для них.

>>> favorite_numbers = [6, 57, 4, 7, 68, 95]
>>> iter(favorite_numbers)
<list_iterator object at 0x7fe8e5623160>

Так же можно использовать встроенную функцию next для итератора, чтобы получить из него следующий элемент (вы получите исключение StopIteration, если кончатся элементы в итераторе).

>>> favorite_numbers = [6, 57, 4, 7, 68, 95]
>>> my_iterator = iter(favorite_numbers)
>>> next(my_iterator)
6
>>> next(my_iterator)
57

Есть еще одно правило, которое делает все более интересным: итераторы сами по себе также являются итерируемыми объектами. Я объяснил последствия этого более подробно в докладе Loop Better, о которой я упоминал выше.

Зачем нужны итераторы?

Итераторы позволяют вам сделать поочередно перебрать элементы, которые будут вычисляется по мере их поступления. Использование итератора вместо списка list, набора set или другой итерируемой структуры данных может иногда позволить нам сэкономить память. Например, мы можем использовать itertools.repeat, чтобы создать итерируемый объект с большим количество элементов:

>>> from itertools import repeat
>>> lots_of_fours = repeat(4, times=100_000_000)

Этот итератор занимает всего 56 байт памяти на моей машине:

>>> import sys
>>> sys.getsizeof(lots_of_fours)
56

Эквивалентный список из 400 миллионов элементов занимает гораздо больше памяти:

>>> lots_of_fours = [4] * 100_000_000
>>> import sys
>>> sys.getsizeof(lots_of_fours)
800000064

Хотя итераторы могут экономить память, они также могут экономить время. Например, если вы хотите распечатать только первую строку 10-гигабайтного файла логов, вы можете легко сделать так:

>>> print(next(open('giant_log_file.txt')))
This is the first line in a giant file

Файловые объекты в Python реализованы то же как итераторы. При прохождению по файлу данные считываются в память по одной строке за раз. Если бы мы вместо этого использовали метод readlines для хранения всех строк в памяти, мы могли бы быстро израсходовать все системную память.

Таким образом, итераторы могут сэкономить нам память, а иногда итераторы также могут сэкономить нам время.

Кроме того, у итераторов есть способности, которых нет у итерируемых объектов. Например, ленивые итерактивные объекты может использоваться для создания итераторов, которые имеют неизвестную длину. На самом деле, вы можете даже создавать бесконечно длинные итераторы.

Например, утилита itertools.count предоставит нам итератор, который будет возвращать каждый раз число от 0 и выше:

>>> from itertools import count
>>> for n in count():
...     print(n)
...
0
1
2
(это будет продолжаться бесконечно)

Создание итератора: объектно-ориентированный путь

Итак, мы увидели, что итераторы могут сэкономить нам память, и сэкономить процессорное время.

Давайте создадим наш собственный итератор. А начнем мы с созданием объекта итератора такого же как itertools.count.

Вот итератор Count, реализованный с использованием класса:

class Count:

    """Iterator that counts upward forever."""

    def __init__(self, start=0):
        self.num = start

    def __iter__(self):
        return self

    def __next__(self):
        num = self.num
        self.num += 1
        return num

Этот класс имеет конструктор, который инициализирует наше текущее число в 0 (или что-либо переданное в качестве start). То, что делает этот класс в качестве итератора, это методы __iter__ и __next__.

Когда итерируемый объект передается во встроенную функцию str, вызывается его метод __str__. Когда объект передается во встроенную функции len, вызывается его метод __len__.

>>> numbers = [1, 2, 3]
>>> str(numbers), numbers.__str__()
('[1, 2, 3]', '[1, 2, 3]')
>>> len(numbers), numbers.__len__()
(3, 3)

Встроенной функции iter для итерируемого объекта попытается вызвать его метод __iter__. Встроенная функции next для объекта попытается вызвать его метод __next__.

Предполагается, что функция iter возвращает итератор. Поэтому наша функция __iter__ должна возвращать итератор. Но наш объект сам является итератором, поэтому должен вернуть себя. Поэтому наш объект Count возвращает self из своего метода __iter__.

Функция next должна возвращать следующий элемент в нашем итераторе или вызывать исключение StopIteration, когда элементов больше нет. Мы возвращаем текущий номер и увеличиваем его, чтобы он был больше во время следующего вызова __next__.

Мы можем вручную запустить наш класс итератора Count следующим образом:

>>> c = Count()
>>> next(c)
0
>>> next(c)
1

Мы также можем использовать объект Count в цикле for, как с любым другим итератором:

>>> for n in Count():
...     print(n)
...
0
1
2
(это будет продолжаться бесконечно)

Это хороший объектно-ориентированный подход к созданию итератора, но это не обычный способ, которым программисты Python делают итераторы. Обычно, когда нам нужен итератор, мы создаем генератор.

Генераторы: простой способ сделать итератор

Самый простой способ создать свои собственные итераторы в Python — это создать генератор.

Есть два способа сделать генераторы в Python.

Возьмем в качестве источника данных этот список номеров:

>>> favorite_numbers = [6, 57, 4, 7, 68, 95]

Мы можем легко создать генератор, который будет лениво возвращать нам все квадраты этих чисел, например:

>>> def square_all(numbers):
...     for n in numbers:
...         yield n**2
...
>>> squares = square_all(favorite_numbers)

Или мы можем сделать такой же генератор, как этот:

>>> squares = (n**2 for n in favorite_numbers)

Первая называется функцией генератора, а вторая называется выражением генератора.

Оба этих объекта-генератора работают одинаково. Оба имеют тип генератора и оба являются итераторами, которые возвращают квадраты чисел из нашего списка чисел.

>>> type(squares)
<class 'generator'>
>>> next(squares)
36
>>> next(squares)
3249

Мы рассмотрим обоих этих подходах к созданию генератора подробнее, но сначала поговорим о терминологии.

Слово «генератор» используется в Python довольно часто:

  • Генератор, также называемый объект генератор, является итератором, тип которого является generator.
  • Функция генератора — это специальный синтаксис, который позволяет нам создать функцию, которая возвращает объект генератора, когда мы его вызываем.
  • Выражение генератора — это синтаксис, похожий на синтаксис генератора списков (comprehension list), который позволяет создавать встроенный объект генератора.

С учетом этой терминологии давайте рассмотрим каждую из этих вещей в отдельности. Сначала рассмотрим функции генератора.

Функции генератора

Функции генератора отличаются от обычных функций тем, что они имеют один или несколько операторов yield.

Обычно, когда вы вызываете функцию, ее код сразу выполняется:

>>> def gimme4_please():
...     print("Let me go get that number for you.")
...     return 4
...
>>> num = gimme4_please()
Let me go get that number for you.
>>> num
4

Но если в функции есть оператор yield, она больше не является типичной функцией. Теперь это функция генератора, то есть она будет возвращать объект генератора при вызове. Этот объект генератора может быть зациклен при выполнение, пока не будет достигнут оператор yield:

что это такое и как использовать

Часто Data Scientist и python-программист сталкиваются с задачей чтения больших объемов данных (Big Data). Чтобы при этом компьютер не зависал, помогут специальные объекты: итератор (iterator) и генератор (generator). В этой статье рассмотрим, что это такое, зачем и как их создавать, а также каким образом они берегут оперативную память.

Iterable, iterator, generator – базовые концепты Python

В предыдущей статье мы затрагивали тему итерируемых структур данных – последовательностей. На практике последовательность соответствует понятию Iterable – объекту-контейнеру, над которым можно провести итерирование. В основном, он используется в конструкции цикла for … in. Списки, словари, множества, массив байтов (bytearray), строки и прочие подобные структуры данных – все это объекты iterable.
У объекта iterable есть метод __iter__(), который возвращает Iterator. Iterator – это объект, реализующий метод __next__(), возвращающий следующий элемент контейнера. Допустим, у нас есть список чисел, и мы хотим пройтись по нему:

nums = [1, 2, 3, 4]
for num in nums:
    print(num)

В данном цикле конструкция in nums вызывает метод __iter__(), который возвращает итератор. А num – это возвращаемый методом __next__() элемент этого итератора. Итерирование прекратится в тот момент, когда возникнет исключение StopIteration, о котором мы расскажем чуть позже.
Объект Generator – это разновидность итератора, который можно проитерировать лишь один раз. Это означает, что второй раз использовать цикл for … in для генератора уже невозможно. Чтобы получить генератор используется ключевое слово yield. Разберем все поподробней.
Что такое итератор: пример
Реализуем обратный счетчик CountDown, который ведет отчет от заданного числа до 0. Для этого нам понадобятся вышерассмотренные методы __iter__() и __next__(). Первый из них возвращает сам объект, а второй – элемент счетчика:

class CountDown:
    def __init__(self, start):
        self.count = start + 1
    def __iter__(self): 
        return self 
    def __next__(self): 
        self.count -= 1 
        if self.count < 0: 
            raise StopIteration 
        return self.count

Здесь в конструкторе __init__() добавляется единица, чтобы вывести еще стартовое число. Инициализируем в качестве стартового значения число 5:

>>> counter = CountDown(5)
>>> for i in counter:
...    print(i)
5
4
3
2
1
0

После того как count станет меньше нуля, итерирование прекращается, так как возникает исключение StopIteration.

Как работает генератор: примеры кода

Как сказано в документации Python [1], генератор – это удобный способ реализовать протокол итератора, так как нет необходимости создавать классы. Представим тот же CountDown в виде генератора:

def countdown(start):
count = start + 1
while count > 0:
yield count
count -= 1

С тем же результатом:

>>> counter = countdown(5)
>>> for i in counter:
...    print(i)
5
4
3
2
1
0

Такая функция ведет себя как обычный итератор, а yield возвращает объект генератора. Ключевое слово yield можно сравнить с return, но yield сохраняет текущее состояние локальных переменных. Следующее обращение к генератору вызывает метод __next__() , который возобновляет работу строк, стоящих после yield, с сохранёнными локальными переменными. Работа будет выполняться до появления ключевого слова yield. В нашем примере всего один yield, находящийся в цикле.

Генераторы в классах

Подчеркнем, в классах тоже можно использовать генератор:

class Countdown:
    def __init__(self, start):
        self.count = start
    def __iter__(self): 
        while self.count > -1: 
            yield self.count 
            self.count -= 1

Так, вместо метода __next__() используется генератор. Такая запись намного короче.
Поясним, почему генераторы и итераторы так эффективны.

В чем польза генераторов Python

Вначале статьи мы упомянули, что последовательности – это iterable; а списки – это последовательности. При этом в примерах Python-кода не создавали ни списки, ни множества. Сам yield возвращал только одно число из последовательности! Отсюда и эффективность – не нужно хранить в памяти всю последовательность, достаточно лишь текущего значения.
Как мы разбирали в прошлой статье, списки в Python можно создавать в одну строчку, используя конструкцию List comprehension. С генераторами тоже можно проделывать подобное:

counter = (i for i in range(5,-1,-1))

В отличие от List comprehension, здесь используются круглые скобки. Также можно проверить тип созданного объекта:

>>> type(counter) 
generator 
>>> for i in counter:
...    print(i)
5
4
3
2
1
0

Теперь вы знаете, что такое итераторы и генераторы, и как они помогают эффективно читать большие данные, включая те, что не помещаются в оперативную память. Однако, стоит помнить, что прочитать их можно только один раз.
 
Освоить все тонкости практической работы с большими данными на Python, помогут наши специализированные курсы по Python в лицензированном учебном центре обучения и повышения квалификации ИТ-специалистов в Москве.

Источники

  1. https://docs.python.org/3/library/stdtypes.html#generator-types

Итерируемый объект, итератор и генератор в Python

В Python итерируемый объект (iterable или iterable object), итератор (iterator или iterator object) и генератор (generator или generator object) — разные понятия, а не синонимы одного и того же. От итерируемого объекта можно получить его «копию»-итератор; генератор является разновидностью итератора.

В некоторых источниках итератор рассматривается как частный случай итерируемого объекта, поскольку оба поддерживают операцию итерации, то есть обход циклом for. Однако for работает только с итераторами. Переданный на обработку объект должен иметь метод __iter__(), который for неявно вызывает перед обходом. Метод __iter__() должен возвращать итератор.

У итерируемого объекта, то есть объекта, который можно «превратить» в итератор, должен быть метод __iter__(), который возвращает соответствующий объект-итератор.

>>> a = [1, 2]
>>> b = a.__iter__() 
>>> a
[1, 2]
>>> b
<list_iterator object at 0x7f7e24c1abe0>
>>> type(a)
<class 'list'>
>>> type(b)
<class 'list_iterator'>

У итерируемого объекта нет метода __next__(), который используется при итерации:

>>> a.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute '__next__'

У итератора есть метод __next__(), который извлекает из итератора очередной элемент. При этом этот элемент уже не содержится в итераторе. Таким образом, итератор в конечном итоге опустошается:

>>> b.__next__()
1
>>> b.__next__()
2
>>> b.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

Метод __next__() исчерпанного итератора возбуждает исключение StopIteration.

У итераторов, также как у итерируемых объектов, есть метод __iter__(). Однако в данном случае он возвращает сам объект-итератор:

>>> a = [1, 2]
>>> a = "hi"
>>> b = a.__iter__()
>>> c = b.__iter__()
>>> a
'hi'
>>> b
<str_iterator object at 0x7f7e24c1ad30>
>>> c
<str_iterator object at 0x7f7e24c1ad30>
>>> b.__next__()
'h'
>>> c.__next__()
'i'
>>> b.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

Здесь переменные b и c указывают на один и тот же объект.

Примеры итерируемых объектов в Python — список, словарь, строка и другие контейнерные типы (они же коллекции), тип, возвращаемый функцией range(). 

Примеры итераторов — файловые объекты, генераторы, итераторы созданные на основе списка, строки, объекта типа range и т. д.

В Python есть встроенные функции iter() и next(), которые соответственно вызывают методы __iter__() и __next__() объектов, переданных в качестве аргумента.

>>> a = {1: 'a', 2: 'b'}
>>> b = iter(a)
>>> b
<dict_keyiterator object at 0x7f7e24c17778>
>>> next(b)
1

Внутренний механизм цикла for сначала вызывает метод __iter__() объекта. Так что, если передан итерируемый объект, создается итератор. После этого применяется метод __next__() до тех пор, пока не будет возбуждено исключение StopIteration.

Поскольку метод __iter__() итератора возвращает сам итератор, то после перебора циклом for объект исчерпывается. То есть получить данные из итератора можно только один раз. В случае с коллекциями это не так. Здесь создается другой объект — итератор. Он, а не итерируемый объект, отдается на обработку циклу for.

>>> a = range(2)
>>> b = iter(a)
>>> type(a)
<class 'range'>
>>> type(b)
<class 'range_iterator'>
>>> for i in a:
...     print(i)
... 
0
1
>>> for i in a:
...     print(i)
... 
0
1
>>> for i in b:
...     print(i)
... 
0
1
>>> for i in b:
...     print(i)
... 
>>> 

Отличительной особенностью генераторов является то, что они создаются не на основе классов, а путем вызова функции, содержащей инструкцию yield, или специальным генераторным выражением по синтаксису похожим на генератор списка. Отметим, генератор списка, который является особым выражением, к генераторам, которые являются разновидностью объектов-итераторов, отношения не имеет. Подробнее можно почитать здесь. 

Другими словами, если потребуется создать свой итератор, может оказаться проще определить функцию с yield или воспользоваться выражением, чем создавать класс с методами __next__() и __iter__().

Рассмотрим пример. Определим сначала собственный класс-итератор:

from random import random


class RandomIncrease:
    def __init__(self, quantity):
        self.qty = quantity
        self.cur = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.qty > 0:
            self.cur += random()
            self.qty -= 1
            return round(self.cur, 2)
        else:
            raise StopIteration


iterator = RandomIncrease(5)
for i in iterator:
    print(i)

Пример выполнения кода:

0.65
1.17
1.19
1.45
2.11

Наш итератор выдает числа по нарастающей. При этом каждое следующее число больше предыдущего на случайную величину.

Здесь же отметим преимущество итераторов как таковых перед контейнерными типами вроде списков. В памяти компьютера не хранятся все элементы итератора, в основном лишь описание, как получить следующий элемент. Если представить, что нужны тысячи чисел или надо генерировать сложные объекты, выгода существенна.

В случае с функцией, создающей генератор, приведенный выше пример может выглядеть так:

def random_increase(quantity):
    cur = 0
    while quantity > 0:
        cur += random()
        quantity -= 1
        yield round(cur, 2)


generator = random_increase(5)
for i in generator:
    print(i)

Нам незачем самим определять методы __iter__() и __next__(), так как они неявно присутствуют у генератора.

Если логика генератора проста, вместо функции можно использовать выражение, создающее генератор:

generator = (round(random()+i, 2) for i in range(5))

for i in generator:
    print(i)

Данный пример не идентичен приведенным выше функции и классу. Здесь целая часть каждого следующего числа больше чем у предыдущего на единицу.

Генераторное выражение и функция-генератор возвращают объект одного и того же типа — generator.

20 приемов использования в Python модуля itertools

В декабре 2019 года мы подробно рассказали о модуле collections. Другой важный компонент стандартной библиотеки – itertools.

Модуль itertools содержит строительные блоки итераторов, основанные на конструкциях из языков программирования APL, Haskell и SML. Ниже мы опишем набор быстрых и эффективных в отношении памяти инструментов, полезных как самостоятельно, так и в сочетании. Вместе они образуют «алгебру итераторов» для программ на чистом Python.

Цель публикации – в сжатой форме рассмотреть распространённые примеры и шаблоны использования модуля itertools.

Начнем с импорта:

        import itertools
    

Чтобы лучше запомнить функции модуля, мы не станем использовать конструкцию from ... import *, а будем обращаться к методам модуля через его имя.

Если вы владеете Jupyter Notebook, блокнот этой статьи доступен на GitHub. Соответственно код легко запустить в интерактивном режиме с помощью Colab.

Функция itertools.count(start=0, step=1) создаёт бесконечный итератор. Можно задать начальное значение и шаг итерирования.

        >>> cnt = itertools.count(start=2020, step=4)
>>> next(cnt)
2020
>>> next(cnt)
2024
>>> next(cnt)
2028
    

Пример использования итератора в zip-функции:

        >>> days = [366]*4
>>> list(zip(itertools.count(2020, 4), days))
[(2020, 366), (2024, 366), (2028, 366), (2032, 366)]
    

Чтобы продолжить счёт при прерывании выполнения программы передайте последнее значение новому объекту итератора в виде параметра start.

Если необходимо подсчитывать число вхождений элементов в список или кортеж, обратите внимание на Counter() из модуля collections.

Если последовательности имеют неодинаковую длину, zip() ограничивается самой короткой:

        >>> list(zip(range(0, 10), range(0, 5)))
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]
    

Но такое сокращение может быть неудобно из-за потери информации. Чтобы сохранить обе последовательности, используйте itertools.zip_longest():

        for (i, j) in itertools.zip_longest(range(0, 10), range(0, 5)):
    print(i, j)
    
        0 0
1 1
2 2
3 3
4 4
5 None
6 None
7 None
8 None
9 None
    

Вместо None функция может подставлять значение, переданное аргументу fillvalue.

Суммирование нарастающим (накопительным) итогом – вид сложения последовательности чисел. Например, так считается квартальная прибыль Каждый элемент складывается с суммой всех предшествовавших элементов. В следующем примере 1 и 2 даёт 3, сумма 1, 2 и 3 равна 6 и т. д. Описанный тип работы с последовательностью воплощен в itertools.accumulate(iterable, func=operator.add, *, initial=None):

        >>> list(itertools.accumulate(range(1, 10)))
[1, 3, 6, 10, 15, 21, 28, 36, 45]
    

Чтобы вывести данные, мы используем список. При печати самого итератора выводится только его ссылка.

По умолчанию к элементам применяется operator.add. Можно, например, указать оператор умножения:

        >>> import operator
>>> list(itertools.accumulate(range(1, 10), operator.mul))
[1, 2, 6, 24, 120, 720, 5040, 40320, 362880]
    

С помощью itertools.cycle() создаётся кольцевой итератор. Прийдя к последнему значению, он вновь начинает с первого:

        >>> waltz = itertools.cycle(['и раз', 'и два', 'и три'])
>>> next(waltz)
'и раз'
>>> next(waltz)
'и два'
>>> next(waltz)
'и три'
>>> next(waltz)
'и раз'
    

Итератор, создаваемый itertools.repeat() это вырожденный случай itertools.cycle(). Вместо последовательности повторяется одно и то же значение. Бесконечно или times раз:

        >>> s = "Птица Говорун отличается умом и сообразительностью"
>>> rep = itertools.repeat(s, times=2)
>>> next(rep)
'Птица Говорун отличается умом и сообразительностью'
>>> next(rep)
'Птица Говорун отличается умом и сообразительностью'
>>> next(rep)
StopIteration...


    

Классический пример использования itertools.repeat() – итератор для map():

        >>> nums = range(10)
>>> squares = map(pow, nums, itertools.repeat(2))
>>> list(squares)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
    

Все числа последовательности nums возведены в степень 2. Итератор вызывается столько раз, сколько необходимо, не нужно думать о числе элементов в последовательности.

Раз мы заговорили о map(), полезно рассказать и о itertools.starmap(). Этот метод принимает функцию и список кортежей аргументов. Как если бы использовался оператор *, отсюда и название:

        >>> squares = itertools.starmap(pow, [(0, 2), (1, 2), (2, 2)])
>>> list(squares)
[0, 1, 4]
    

Модуль itertools позволяет решать программные задачи, построенные на структурах комбинаторики.

Сочетания – выбранные из множества n объектов комбинации m объектов, отличающиеся хотя бы одним объектом. Порядок элементов не важен.

Например, мы хотим составить трёхцветный флаг из лент цветных тканей. Есть четыре цвета лент. Все варианты выбора тканей без учёта их расположения:

        colors = ['белый', 'жёлтый', 'синий', 'красный']
for item in itertools.combinations(colors, 3):
    print

Модуль itertools

Python предоставляет замечательный модуль для создания собственных итераторов. Я говорю о модуле itertools. Предоставленные данным модулем инструменты быстрые и эффективно используют память. Вы сможете использовать эти кирпичики для создания собственных специализированных итераторов, которые могут использоваться для эффективных циклов. В данной статье мы рассмотрим примеры каждого предоставленного инструмента, так что к её концу вы сможете эффективно использовать каждый инструмент для создания собственных скриптов, а главное, знать, когда именно их применять.

Бесконечные итераторы

Пакет itertools содержит три итератора, которые могут выполнять итерацию бесконечно. Это означает, что когда вы используете их, вам нужно прекрасно понимать, что в конечном итоге вам нужно разорвать эти итераторы, во избежание бесконечного цикла. Это может быть применимо при генерации чисел или при циклическом переключении по итерациям неизвестной длины, например. Давайте начнем с изучения этих итераций!

count(start=0, step=1)

Итератор count возвращает равномерно распределенные переменные, начиная с числа, которое вы передаете в качестве стартового параметра. Он также принимает параметр шага. Давайте взглянем на простой пример:

from itertools import count

for i in count(10):
if i > 20:
break
else:
print(i)



from itertools import count

 

for i in count(10):

    if i > 20:

        break

    else:

        print(i)

Результат:

10
11
12
13
14
15
16
17
18
19
20

Мы импортируем count из itertools и создаем цикл for. Мы добавляем условную проверку, которая разорвет цикл, когда итератор превысит отметку 20, в противном случае он выводит, где именно мы находимся в итераторе. Обратите внимание на то, что результат начинается с 10, так как мы именно это и указали в качестве нашего стартового значения. Еще один способ ограничить выдачу данного бесконечного итератора, это использовать наследуемый модуль из itertools, под названием islice. Как это работает:

from itertools import islice

for i in islice(count(10), 5):
print(i)



from itertools import islice

 

for i in islice(count(10), 5):

    print(i)

Результат:

В этом примере мы импортируем islice и зацикливаем по count, начиная с 10 и заканчивая после 5 элементов. Как вы могли догадаться, второй аргумент «когда» нужен для остановки итерации. Но это не означает «остановись, когда я дойду до числа 5», вместо этого «остановись, когда мы достигнем пяти итераций».

cycle(iterable)

Итератор cycle из itertools позволяет вам создавать итератор, которой создает бесконечный цикл ряда значений. Давайте передадим ему строку из трех букв и посмотрим, к чему это приведет:

from itertools import cycle

count = 0
for item in cycle(‘XYZ’):
if count > 7:
break
print(item)
count += 1



from itertools import cycle

 

count = 0

for item in cycle(‘XYZ’):

    if count > 7:

        break

    print(item)

    count += 1

Результат:

Здесь мы создаем цикл for для того, чтобы бесконечно зациклить буквы Z, Y, Z. Конечно, нам не нужен бесконечный цикл на постоянной основе, так что мы добавим простой счетчик для разрыва цикла. Вы также можете использовать встроенный инструмент Python под названием next для итерации над итераторами, которые вы создаете при помощи itertools:

from itertools import cycle

polys = [‘triangle’, ‘square’, ‘pentagon’, ‘rectangle’]
iterator = cycle(polys)

print(next(iterator)) # triangle
print(next(iterator)) # square
print(next(iterator)) # pentagon
print(next(iterator)) # rectangle
print(next(iterator)) # triangle
print(next(iterator)) # square



from itertools import cycle

 

polys = [‘triangle’, ‘square’, ‘pentagon’, ‘rectangle’]

iterator = cycle(polys)

 

print(next(iterator)) # triangle

print(next(iterator)) # square

print(next(iterator)) # pentagon

print(next(iterator)) # rectangle

print(next(iterator)) # triangle

print(next(iterator)) # square

В данном коде мы создаем простой список полигонов и передаем их циклу. Мы сохраняем наш новый итератор в качестве переменной, и затем мы передаем эту переменную нашей следующей функции. Каждый раз, когда мы вызываем функцию, она возвращает следующее значение в итераторе. Так как этот итератор бесконечный, мы можем вызывать next днями напролет, и все равно объекты никогда не закончатся.

repeat(object)

Итератор возвращает объект снова и снова, бесконечно, пока вы не настроите его аргумент times. Это очень похоже на cycle, за исключением того, что он не зацикливает набор значений повторно. Давайте взглянем на простой пример:

from itertools import repeat

iterator = repeat(‘test’, 5)

print(next(iterator)) # test
print(next(iterator)) # test
print(next(iterator)) # test
print(next(iterator)) # test
print(next(iterator)) # test

# В 6й раз будет ошибка…
print(next(iterator))

Traceback (most recent call last):
Python Shell, prompt 21, line 1
builtins.StopIteration:



from itertools import repeat

 

iterator = repeat(‘test’, 5)

 

print(next(iterator)) # test

print(next(iterator)) # test

print(next(iterator)) # test

print(next(iterator)) # test

print(next(iterator)) # test

 

# В 6й раз будет ошибка…

print(next(iterator))

 

Traceback (most recent call last):

  Python Shell, prompt 21, line 1

builtins.StopIteration:

Здесь мы импортируем repeat и указываем ему повторить текст «test» пять раз. Далее мы вызываем next в нашем новом итераторе шесть раз, что бы увидеть, все ли работает правильно. Когда вы запустите этот код, вы увидите, что появится ошибка StopIteration, так как мы превысили количество значений в нашем итераторе

Есть вопросы по Python?

На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!

Telegram Чат & Канал

Вступите в наш дружный чат по Python и начните общение с единомышленниками! Станьте частью большого сообщества!

Паблик VK

Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!


Большая часть итераторов, которые вы создаете при помощи itertools, не являются бесконечными. В этом разделе, мы изучим конечные итераторы itertools. Для получения читабельной выдачи, мы используем встроенный тип списка Python. Если вы не используете список, вы получите только объект itertools в выдаче.

accumulate(iterable)

Итератор accumulate возвращает накопленные суммы или накопленные результаты двух аргументных функций, которые вы передали для накопления. По умолчанию, accumulate – это дополнение, так что давайте посмотрим, как она работает на деле:

from itertools import accumulate

result = list(accumulate(range(10)))
print(result)



from itertools import accumulate

 

result = list(accumulate(range(10)))

print(result)

Результат использования range:

[0, 1, 3, 6, 10, 15, 21, 28, 36, 45]



[0, 1, 3, 6, 10, 15, 21, 28, 36, 45]

Здесь мы импортируем accumulate и передаем ей ряд из 10 чисел от 0 до 9. Он добавляет каждое из них по очереди, начиная с 0, затем 0+1, затем 1+2, и так далее. Давайте импортируем модуль operator и добавим его в смесь:

from itertools import accumulate
import operator

result = list(accumulate(range(1, 5), operator.mul))
print(result) # [1, 2, 6, 24]



from itertools import accumulate

import operator

 

result = list(accumulate(range(1, 5), operator.mul))

print(result) # [1, 2, 6, 24]

Здесь мы передали числа от 1 до 4 в наш итератор accumulate. Мы также передаем ему функцию operator.mul. Эти функции принимают аргументы, для возможности умножения. Так что в каждой итерации она не суммирует, а умножает (1×1=1, 1×2=2, 2×3=6, и т.д.). Документация accumulate показывает несколько других интересных примеров, таких как амортизация и хаотичное рекуррентное соотношение. Вам определенно следует изучить эти примеры, так как они могут оказаться весьма полезными на практике.

chain(*iterables)

Итератор chain берет ряд итерируемых, и лепит из них одну длинную итерируемую. Недавно это очень выручило меня во время работы над одним проектом. В общем, у нас есть список с определенными объектами, и два других списка, которые нам нужно добавить в общий список, но нам нужно только присоединить объекты каждого списка с объектами общего списка, а не создавать список списков. Изначально я попробовал сделать как-то так:

my_list = [‘foo’, ‘bar’]
numbers = list(range(5))
cmd = [‘ls’, ‘/some/dir’]

my_list.extend(cmd, numbers)

print(my_list)
# [‘foo’, ‘bar’, [‘ls’, ‘/some/dir’], [0, 1, 2, 3, 4]]



my_list = [‘foo’, ‘bar’]

numbers = list(range(5))

cmd = [‘ls’, ‘/some/dir’]

 

my_list.extend(cmd, numbers)

 

print(my_list)

# [‘foo’, ‘bar’, [‘ls’, ‘/some/dir’], [0, 1, 2, 3, 4]]

К сожалению, это не сработало так, как я ожидал. Модуль itertools предоставляет более элегантный способ совмещения этих списков в один при помощи chain:

from itertools import chain

numbers = list(range(5))
cmd = [‘ls’, ‘/some/dir’]

my_list = list(chain([‘foo’, ‘bar’], cmd, numbers))

print(my_list)
# [‘foo’, ‘bar’, ‘ls’, ‘/some/dir’, 0, 1, 2, 3, 4]



from itertools import chain

 

numbers = list(range(5))

cmd = [‘ls’, ‘/some/dir’]

 

my_list = list(chain([‘foo’, ‘bar’], cmd, numbers))

 

print(my_list)

# [‘foo’, ‘bar’, ‘ls’, ‘/some/dir’, 0, 1, 2, 3, 4]

Самые проницательные мои читатели могут заметить, что здесь виден другой способ, которым можно достичь той же цели без использования itertools. Вы можете сделать следующее, для получения аналогичного результата:

my_list = [‘foo’, ‘bar’]
my_list += cmd + numbers

print(my_list)
# [‘foo’, ‘bar’, ‘ls’, ‘/some/dir’, 0, 1, 2, 3, 4]



my_list = [‘foo’, ‘bar’]

my_list += cmd + numbers

 

print(my_list)

# [‘foo’, ‘bar’, ‘ls’, ‘/some/dir’, 0, 1, 2, 3, 4]

Оба этих метода определенно работают, и до того, как я узнал о chain, я бы выбрал второе направление, но я думаю, что chain более прямой и простой способ понимания решения данного случая.

chain.from_iterable(iterable)

Вы также можете использовать метод chain под названием from_iterable. Этот метод работает немного иначе, не используя chain напрямую. Вместо передачи ряда итерируемых вам нужно передать вложенный список. Давайте посмотрим:

from itertools import chain

numbers = list(range(5))
cmd = [‘ls’, ‘/some/dir’]

data = list(chain.from_iterable([cmd, numbers]))
print(data) # [‘ls’, ‘/some/dir’, 0, 1, 2, 3, 4]



from itertools import chain

 

numbers = list(range(5))

cmd = [‘ls’, ‘/some/dir’]

 

data = list(chain.from_iterable([cmd, numbers]))

print(data) # [‘ls’, ‘/some/dir’, 0, 1, 2, 3, 4]

compress(data, selectors)

Наследуемый модуль compress полезен при фильтрации первой итерируемой со второй. Это работает путем превращения второй итерируемой в список с Boolean (или с единицами и нулями, что одно и тоже). Как это работает:

from itertools import compress

letters = ‘ABCDEFG’
bools = [True, False, True, True, False]

data = list(compress(letters, bools))
print(data) # [‘A’, ‘C’, ‘D’]



from itertools import compress

 

letters = ‘ABCDEFG’

bools = [True, False, True, True, False]

 

data = list(compress(letters, bools))

print(data) # [‘A’, ‘C’, ‘D’]

В данном примере у нас есть группа из семи букв и список из True и False. Далее мы передаем их функции compress. Функция compress пройдете через каждую соответствующую итерируемую, и сравнит первую со второй. Если вторая получает оценку True, то значит что с ней все хорошо. Если False, то этот объект будет удален. Таким образом, если вы изучите приведенный выше пример, вы увидите, что у нас True на первой, третьей и четвертой позициях, что соответствует A,C и D.

dropwhile(predicate, iterable)

У нас в распоряжении имеется небольшой итератор itertools под названием dropwhile. Этот малыш может удалять элементы, если критерием фильтра является True. По этой причине, вы можете не увидеть никакой выдачи из этого итератора, пока предикат не станет False. Это может затянуть время запуска, а нам этого не нужно. Давайте посмотрим на пример из документации Python.

from itertools import dropwhile

data = list(dropwhile(lambda x: x<5, [1,4,6,4,1]))
print(data) # [6, 4, 1]



from itertools import dropwhile

 

data = list(dropwhile(lambda x: x<5, [1,4,6,4,1]))

print(data) # [6, 4, 1]

Здесь мы импортируем dropwhile, затем передаем его простому оператору lambda. Эта функция выдает True, если значение х меньше или равно 5. В противном случае она вернет False. Функция dropwhile создаст цикл над списком и передаст каждый элемент лямбде. Если лямбда возвращает True, тогда значение будет удалено. Как только мы достигнем цифры 6, лямбда вернет False, и мы сохраняем число 6 и все значения, которые за ним следуют. Я нашел это весьма полезным в применении обычной функции над лямбдой, когда я исследую что-нибудь новое. Так что давайте перевернем ситуацию с ног на голову и создадим функцию, которая возвращает True, если значение превышает число 5.

from itertools import dropwhile

def greater_than_five(x):
return x > 5

data = list(
dropwhile(
greater_than_five,
[6, 7, 8, 9, 1, 2, 3, 10]
)
)

print(data) # [1, 2, 3, 10]



from itertools import dropwhile

 

def greater_than_five(x):

    return x > 5

 

data = list(

    dropwhile(

        greater_than_five,

        [6, 7, 8, 9, 1, 2, 3, 10]

    )

)

 

print(data) # [1, 2, 3, 10]

Здесь мы создали простую функцию в интерпретаторе Python. Это функция является нашим предикатом (или фильтром). Если переданные нами значения отмечены как True, то они будут удалены. Как только мы достигнем значения меньше 5-и, то ВСЕ значения после и включая это значение сохранятся, что мы и видим в примере выше.

filterfalse(predicate, iterable)

Функция itertools под названием filterfalse очень похожа на dropwhile. Однако, вместо сброса значений, отмеченных как True, filterfalse только вернет те значения, которые оцениваются как False. Давайте используем нашу функцию из предыдущего раздела:

from itertools import filterfalse

def greater_than_five(x):
return x > 5

data = list(
filterfalse(
greater_than_five,
[6, 7, 8, 9, 1, 2, 3, 10]
)
)

print(data) # [1, 2, 3]



from itertools import filterfalse

 

def greater_than_five(x):

    return x > 5

 

data = list(

    filterfalse(

        greater_than_five,

        [6, 7, 8, 9, 1, 2, 3, 10]

    )

)

 

print(data) # [1, 2, 3]

Здесь мы передаем filterfalse нашей функции и список чисел. Если число меньше пяти, оно сохраняется. В противном случае, оно отбрасывается. Вы заметите, что наш результат только 1, 2 и 3. В отличие от dropwhile, filterfalse проверит каждое значение в фильтре.

groupby(iterable, key=None)

Итератор groupby возвращает последовательные ключи и группы из итерируемой. Это трудно понять, не взглянув на пример. Но мы это исправим! Введите следующий код в ваш интерпретатор или сохраните его в файле:

from itertools import groupby

vehicles = [(‘Ford’, ‘Taurus’), (‘Dodge’, ‘Durango’),
(‘Chevrolet’, ‘Cobalt’), (‘Ford’, ‘F150’),
(‘Dodge’, ‘Charger’), (‘Ford’, ‘GT’)]

sorted_vehicles = sorted(vehicles)

for key, group in groupby(sorted_vehicles, lambda make: make[0]):
for make, model in group:
print(‘{model} is made by {make}’.format(model=model, make=make))

print («**** END OF GROUP ***\n»)



from itertools import groupby

 

vehicles = [(‘Ford’, ‘Taurus’), (‘Dodge’, ‘Durango’),

            (‘Chevrolet’, ‘Cobalt’), (‘Ford’, ‘F150’),

            (‘Dodge’, ‘Charger’), (‘Ford’, ‘GT’)]

 

sorted_vehicles = sorted(vehicles)

 

for key, group in groupby(sorted_vehicles, lambda make: make[0]):

    for make, model in group:

        print(‘{model} is made by {make}’.format(model=model, make=make))

    

    print («**** END OF GROUP ***\n»)

Здесь мы импортируем groupby и создаем список кортежей. Далее мы сортируем данные, для корректного вывода, а также, чтобы позволить groupby группировать объекты корректным образом. Далее, мы зацикливаем выданный groupby итератор, который дает нам ключ и группу. Затем мы зацикливаем группу и выводим то, что в ней. Если вы запустите этот код, вы увидите что-то вроде следующего:

Cobalt is made by Chevrolet
**** END OF GROUP ***

Charger is made by Dodge
Durango is made by Dodge
**** END OF GROUP ***

F150 is made by Ford
GT is made by Ford
Taurus is made by Ford
**** END OF GROUP ***



Cobalt is made by Chevrolet

**** END OF GROUP ***

 

Charger is made by Dodge

Durango is made by Dodge

**** END OF GROUP ***

 

F150 is made by Ford

GT is made by Ford

Taurus is made by Ford

**** END OF GROUP ***

Ради смеха, попробуйте изменить код, передав vehicles вместо sorted_vehicles. Так вы сразу поймете, зачем нужно сортировать данные перед их запуском в groupby.

islice(iterable, start, stop)

Ранее мы упоминали islice в разделе count. Давайте немного углубимся в данный вопрос. Итератор islice возвращает указанные элементы из итерируемой. Это своего рода непрозрачный оператор. В целом, islice использует срез индекса вашей итерируемой (тот или иной объект, над которым вы выполняете итерацию) и возвращает выбранный объект в качестве итератора. Существует две реализации islice.

itertools.islice(iterable, stop) и новая версия islice, которая более соответствует обычному слайсингу Python: islice(iterable, start, stop[, step]). Давайте рассмотрим первую версию, чтобы понять, как это работает:

from itertools import islice

iterator = islice(‘123456’, 4)

print(next(iterator)) # 1
print(next(iterator)) # 2
print(next(iterator)) # 3
print(next(iterator)) # 4

print(next(iterator)) # ошибка!

Traceback (most recent call last):
Python Shell, prompt 15, line 1
builtins.StopIteration:



from itertools import islice

 

iterator = islice(‘123456’, 4)

 

print(next(iterator)) # 1

print(next(iterator)) # 2

print(next(iterator)) # 3

print(next(iterator)) # 4

 

print(next(iterator)) # ошибка!

 

Traceback (most recent call last):

   Python Shell, prompt 15, line 1

builtins.StopIteration:

В данном коде мы передаем строку из шести символов нашему islice совместно с числом 4, который является стоп-аргументом. Это значит, что итератор, возвращаемый islice, будет содержать первые 4 объекта в содержащейся в нем строке. Мы можем проверить это, вызвав next в нашем итераторе четыре раза, что мы и делали выше. Python достаточно продуманный, чтобы понять, что если мы имеем только два переданных islice аргумента, то второй аргумент является стоп-аргументом. Давайте попробуем передать ему три аргумента, чтобы продемонстрировать, что вы можете передать ему стартовый и стоп аргументы. Инструмент itertools под названием count поможет нам продемонстрировать данную задумку:

from itertools import islice
from itertools import count

for i in islice(count(), 3, 15):
print(i)



from itertools import islice

from itertools import count

 

for i in islice(count(), 3, 15):

    print(i)

Результат:

3
4
5
6
7
8
9
10
11
12
13
14

Здесь мы вызвали count и указали islice, что мы начинаем с числа 3 и заканчиваем тогда, когда достигнем числа 15. Это очень похоже на слайсинг, за исключением того, что мы делаем это с итератором и получаем новый итератор!

starmap(function, iterable)

Инструмент starmap создает итератор, который может проводить вычисления, используя функцию и итерируемый. В документации отмечают следующее:

Разница между map() и starmap() параллельна различию между функцией (a,b) и функцией (*с).

Давайте взглянем на простой пример:

from itertools import starmap

def add(a, b):
return a+b

for item in starmap(add, [(2,3), (4,5)]):
print(item)



from itertools import starmap

 

def add(a, b):

    return a+b

 

for item in starmap(add, [(2,3), (4,5)]):

    print(item)

Здесь мы создаем простую функцию добавления, которая принимает два аргумента. Далее мы создаем цикл for и вызываем starmap с функцию в его первом аргументе и списком кортежей для итерируемой. Функция starmap передает все объекты кортежей функции и возвращает итератор результатов, который мы выводим.

takewhile(predicate, iterable)

Модуль takewhile это как рассматриваемый нами dropwhile, только наоборот. Модуль takewhile создает итератор, который возвращает элементы из итерируемой до тех пор, пока наш предикат или фильтр оцениваются как True. Давайте рассмотрим небольшой пример, чтобы разобраться с тем, как это реализуется:

from itertools import takewhile

data = list(takewhile(lambda x: x<5, [1,4,6,4,1]))
print(data) # [1, 4]



from itertools import takewhile

 

data = list(takewhile(lambda x: x<5, [1,4,6,4,1]))

print(data) # [1, 4]

Здесь мы запускаем takewhile при помощи функции лямбда и списка. Выдача представлена только первыми двумя целыми числами нашей итерируемой. Причина в том, что и 1 и 4 меньше, чем 5, но 6 имеет большее значение. Так что после того, как takewhile столкнется с цифрой 6, условие станет False, и дальнейшие объекты итерируемой будут игнорироваться.

tee(iterable, n=2)

Инструмент tee создает n количество итераторов из одной итерируемой. Это значит, что вы можете создать несколько итераторов из одной итерируемой. Давайте взглянем на следующий код, чтобы понять, что к чему:

from itertools import tee

data = ‘ABCDE’
iter1, iter2 = tee(data)

for item in iter1:
print(item)



from itertools import tee

 

data = ‘ABCDE’

iter1, iter2 = tee(data)

 

for item in iter1:

    print(item)

Результат:

Итераторов Python


Итераторы Python

Итератор — это объект, содержащий счетное количество значений.

Итератор — это объект, который можно повторять, что означает, что вы можете
пройти через все значения.

Технически в Python итератор — это объект, реализующий
протокол итератора, состоящий из методов __iter __ ()
и __next __ () .


Итератор против итерабельного

Списки, кортежи, словари и наборы — все это повторяемые объекты.Они повторяются
контейнеров , из которых можно получить итератор.

Все эти объекты имеют метод iter () , который используется для получения итератора:

Пример

Вернуть итератор из кортежа и вывести каждое значение:

mytuple = («яблоко», «банан», «вишня»)
myit = iter (mytuple)

print (next (myit))
print (next (myit))
print (next (myit))

Попробуй сам »

Четные строки являются повторяемыми объектами и могут возвращать итератор:

Пример

Строки также являются повторяемыми объектами, содержащими последовательность символов:

mystr = «banana»
myit = iter (mystr)

print (next (myit))
print (next (myit))
print (next (myit))
print (next (myit))
print (next (myit))
print (next (myit))

Попробуй сам »


Зацикливание через итератор

Мы также можем использовать цикл для для итерации по итерируемому объекту:

Пример

Итерировать значения кортежа:

mytuple = («яблоко», «банан», «вишня»)

для x в mytuple:
печать (x)

Попробуй сам »

Пример

Итерировать символы строки:

mystr = «banana»

для x в mystr:
печать (x)

Попробуй сам »

Цикл для фактически создает объект итератора и выполняет next ()
метод для каждого цикла.



Создать итератор

Чтобы создать объект / класс в качестве итератора, вы должны реализовать методы
__iter __ () и
__next __ ()
к вашему объекту.

Как вы узнали из Python
В главе «Классы / объекты» все классы имеют функцию, называемую
__init __ () , что позволяет делать некоторые
инициализация при создании объекта.

Метод __iter __ () действует аналогично, вы можете
выполнять операции (инициализация и т. д.), но всегда должен возвращать объект-итератор
сам.

Метод __next __ () также позволяет выполнять
операций и должен возвращать следующий элемент в последовательности.

Пример

Создайте итератор, который возвращает числа, начиная с 1, и каждую последовательность
увеличится на единицу (возврат 1,2,3,4,5 и т. д.):

class MyNumbers:
def __iter __ (self):
self.a =
1
возврат self

def __next __ (self):
х = сам.a
self.a + = 1
return x

myclass = MyNumbers ()
myiter = iter (myclass)

print (next (myiter))
print (next (myiter))
print (next (myiter))
print (next (myiter))
print (следующий (myiter))

Попробуй сам »


StopIteration

Приведенный выше пример будет продолжаться вечно, если у вас будет достаточно операторов next () или если он будет использован в
для петли .

Чтобы итерация не продолжалась вечно, мы можем использовать
StopIteration заявление.

В методе __next __ () мы можем добавить условие завершения, чтобы вызвать ошибку, если итерация выполняется указанное количество раз:

Пример

Остановить после 20 итераций:

class MyNumbers:
def __iter __ (self):
self.a =
1
возврат self

def __next __ (self):
если self.a <= 20:
x = self.a
self.a + = 1
возврат x
else:
поднять StopIteration

myclass =
MyNumbers ()
myiter = iter (myclass)

для x в myiter:
печать (x)

Попробуй сам »


.

python — что такое итератор, итерация и итерация?

Переполнение стека

  1. Около
  2. Продукты

  3. Для команд
  1. Переполнение стека
    Общественные вопросы и ответы

  2. Переполнение стека для команд
    Где разработчики и технологи делятся частными знаниями с коллегами

  3. Вакансии
    Программирование и связанные с ним технические возможности карьерного роста

  4. Талант
    Нанимайте технических специалистов и создавайте свой бренд работодателя

  5. Реклама
    Обратитесь к разработчикам и технологам со всего мира

  6. О компании

Загрузка…

.

Как это использовать и зачем?

Итераторы в Python

Итераторы в Python повсюду. Они элегантно реализованы в для циклов, интерпретаций, генераторов и т. Д., Но скрыты на виду.

Итератор в Python — это просто объект, который можно повторять. Объект, который будет возвращать данные, по одному элементу за раз.

Технически говоря, объект итератора Python должен реализовывать два специальных метода: __iter __ () и __next __ () , которые вместе называются протоколом итератора .

Объект называется итератором , если мы можем получить от него итератор. Большинство встроенных контейнеров в Python, таких как: список, кортеж, строка и т. Д., Являются повторяемыми.

Функция iter () (которая, в свою очередь, вызывает метод __iter __ () ) возвращает от них итератор.


Итератор

Мы используем функцию next () для ручного перебора всех элементов итератора. Когда мы дойдем до конца и больше не будет возвращаемых данных, это вызовет исключение StopIteration Exception.Ниже приводится пример.

  # определить список
my_list = [4, 7, 0, 3]

# получить итератор с помощью iter ()
my_iter = iter (my_list)

# перебираем его, используя next ()

# Вывод: 4
печать (далее (my_iter))

# Вывод: 7
печать (далее (my_iter))

# next (obj) совпадает с obj .__ next __ ()

# Вывод: 0
print (my_iter .__ следующий __ ())

# Вывод: 3
print (my_iter .__ следующий __ ())

# Это вызовет ошибку, ничего не осталось
следующий (my_iter)  

Выход

  4
7
0
3
Отслеживание (последний вызов последний):
  Файл «<строка>», строка 24, в <модуле>
    следующий (my_iter)
StopIteration  

Более элегантный способ автоматической итерации — использование цикла for.Используя это, мы можем перебирать любой объект, который может возвращать итератор, например список, строку, файл и т. Д.

  >>> для элемента в my_list:
... print (элемент)
...
4
7
0
3  

Работа цикла for для итераторов

Как мы видим в приведенном выше примере, цикл для мог автоматически выполнять итерацию по списку.

Фактически цикл для может выполнять итерацию по любой итерации. Давайте подробнее рассмотрим, как цикл для фактически реализован в Python.

  для элемента в итерации:
    # сделать что-нибудь с элементом  

Фактически реализован как.

  # создать объект итератора из этого итератора
iter_obj = iter (повторяемый)

# бесконечная петля
в то время как True:
    пытаться:
        # получить следующий предмет
        element = next (iter_obj)
        # сделать что-нибудь с элементом
    кроме StopIteration:
        # если StopIteration поднят, выйти из цикла
        перерыв  

Таким образом, внутри цикла for создается объект-итератор iter_obj путем вызова iter () на итерируемом объекте.

По иронии судьбы, этот цикл для на самом деле является бесконечным циклом while.

Внутри цикла он вызывает next () , чтобы получить следующий элемент, и выполняет тело цикла for с этим значением. После того, как все элементы исчерпаны, вызывается StopIteration , который захватывается внутри, и цикл заканчивается. Обратите внимание, что любые другие исключения пройдут.


Создание пользовательских итераторов

Создать итератор с нуля в Python легко.Нам просто нужно реализовать методы __iter __ () и __next __ () .

Метод __iter __ () возвращает сам объект итератора. При необходимости можно выполнить некоторую инициализацию.

Метод __next __ () должен возвращать следующий элемент в последовательности. По достижении конца и в последующих вызовах он должен поднять StopIteration .

Здесь мы показываем пример, который дает нам следующую степень двойки на каждой итерации.Показатель мощности начинается с нуля до заданного пользователем числа.

  класс PowTwo:
    "" "Класс для реализации итератора
    степеней двойки "" "

    def __init __ (self, max = 0):
        self.max = max

    def __iter __ (сам):
        self.n = 0
        вернуть себя

    def __next __ (сам):
        если self.n <= self.max:
            результат = 2 ** self.n
            self.n + = 1
            вернуть результат
        еще:
            поднять StopIteration


# создать объект
числа = PowTwo (3)

# создать итерацию из объекта
i = iter (числа)

# Использование next для перехода к следующему элементу итератора
печать (следующий (я))
печать (следующий (я))
печать (следующий (я))
печать (следующий (я))
печать (следующий (i))  

Выход

  1
2
4
8
Отслеживание (последний вызов последний):
  Файл "/ home / bsoyuj / Desktop / Untitled-1.py ", строка 32, в 
    печать (следующий (я))
  Файл «<строка>», строка 18, в __next__
    поднять StopIteration
StopIteration  

Мы также можем использовать цикл для для перебора нашего класса итератора.

  >>> для i в PowTwo (5):
... печать (я)
...
1
2
4
8
16
32  

Бесконечные итераторы Python

Нет необходимости, чтобы элемент в объекте итератора был исчерпан. Итераторов может быть бесконечное количество (которое никогда не заканчивается).Мы должны быть осторожны при обращении с такими итераторами.

Вот простой пример, демонстрирующий бесконечные итераторы.

Встроенная функция iter () Функция может быть вызвана с двумя аргументами, где первый аргумент должен быть вызываемым объектом (функцией), а второй - сигнальным. Итератор вызывает эту функцию до тех пор, пока возвращаемое значение не станет равным сигнальному значению.

  >>> интервал ()
0

>>> inf = iter (число, 1)
>>> следующий (инф)
0
>>> следующий (инф)
0  

Мы видим, что функция int () всегда возвращает 0.Таким образом, передача его как iter (int, 1) вернет итератор, который вызывает int () , пока возвращаемое значение не станет равным 1. Этого никогда не произойдет, и мы получим бесконечный итератор.

Мы также можем создавать собственные бесконечные итераторы. Следующий итератор теоретически вернет все нечетные числа.

  класс InfIter:
    "" "Бесконечный итератор для возврата всех
        нечетные числа"""

    def __iter __ (сам):
        self.num = 1
        вернуть себя

    def __next __ (сам):
        число = себя.число
        self.num + = 2
        return num  

Пробный прогон будет следующим.

  >>> a = iter (InfIter ())
>>> следующий (а)
1
>>> следующий (а)
3
>>> следующий (а)
5
>>> следующий (а)
7  

И так далее ...

Будьте осторожны, чтобы включить условие завершения при повторении этих типов бесконечных итераторов.

Преимущество использования итераторов в том, что они экономят ресурсы. Как показано выше, мы могли получить все нечетные числа, не сохраняя в памяти всю систему счисления.Мы можем иметь бесконечное количество элементов (теоретически) в конечной памяти.

Есть более простой способ создавать итераторы в Python. Чтобы узнать больше, посетите: Генераторы Python с использованием yield.

.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *