Проект для лекции Lecture10.rar.
Мы рассмотрели базисные элементы, составляющие основу программирования на языке Python. Мы понимаем как устроены типы данных языка, какие типы данных являются встроенными. Знаем синтаксис и семантику операторов языка, позволяющих написать программный код. Умеем, надеюсь, программировать в процедурах и функциях. Умеем использовать при построении программного проекта различные стили программирования, включая модульное и объектно-ориентированное программирование в классах и объектах. Понимаем, что при построении программного проекта необходимо обеспечить его корректность и устойчивость. Так что освоение курса "Основы программирования на языке Python" почти завершено.
Понимание основ требует рассмотрения деталей таких полезных и часто используемых инструментов, встроенных в язык, как итераторы, генераторы и декораторы.
Прежде чем попытаться понять, что такое итератор, давайте начнем с более простого понятия - итерация. В переводе с английского итерация - это повторение. Для программиста итерация связана с циклом. Каждый оператор цикла имеет заголовок и тело. Однократное выполнение тела цикла называется итерацией, а процесс выполнения цикла - итерированием. Как правило, в языках программирования встречается три типа цикла:
for, когда число итераций указано в заголовке и задан целочисленный параметр цикла (индекс), получающий на каждой итерации новое значение. Вот пример типичного заголовка цикла for в языках С++, C# - for(int I = 0; I < N; I++). У такого цикла N итераций и параметр I пробегает все значения от 0 до N - 1.While - создается тогда, когда число итераций не известно в начальной точке запуска цикла и не может быть задано в заголовке. Конечно, когда программист пишет цикл While, он предполагает (уверен), что цикл обязательно завершит свою работу.for each - цикл, обрабатывающий некоторую конечную структуру данных (контейнер данных). Число итераций определяется размером контейнера. На каждой итерации обрабатывается очередной элемент данных контейнера. Понятно, что данные контейнера должны быть перечислимы, то есть на данных задан некий порядок, известен начальный элемент и определена функция next (следующий), которая при первом обращении выдает первый по порядку элемент, а затем при каждом обращении выдает следующий элемент в заданном порядке. Предусмотрен также способ уведомления об исчерпании элементов контейнера. Структура данных, обладающая такими свойствами, называется итерируемой структурой данных.Понятие итерированной структуры данных можно обобщить на понятие итерируемого объекта произвольного класса. Так появляется понятие итератора. В Википедии дается следующее определение итератора:
Итератор (от англ. iterator ? перечислитель) - интерфейс, предоставляющий доступ к элементам коллекции (массива или контейнера) и навигацию по ним.
Не самое лучшее определение. Во-первых, в переводе итератор - это повторитель, (перечислитель в английском - это enumerator). Во-вторых, назвать итератор интерфейсом мало что проясняет, поскольку понятие интерфейс в данном контексте не определено. Итератор - это механизм, предоставляющий доступ к элементам коллекции. В разных языках этот механизм может быть различным и зависеть от типа коллекции. В языке Python итератор - это объект класса, обладающего соответствующим интерфейсом. В этой лекции мы подробно поговорим о том, каким именно интерфейсом должен обладать итерируемый класс, у которого существует объект итератор.
В языке Python есть два вида циклов - while и for - in. Цикл while соответствует общепринятому циклу этого вида. Цикл for - in является обобщенным вариантом цикла for - each и позволяет работать с любыми итерируемыми объектами, которые в Python используются гораздо чаще чем в других языках программирования.
Все структуры данных, наследуемые от последовательности, следовательно, перечислимые, имеющие доступ к элементам по индексу, являются итерируемыми по определению. Напомню, что к таким структурам относятся строки, списки, кортежи. Но ни файлы, ни множества, ни словари к таким структурам не относятся. Тем не менее, все перечисленные структуры являются итерируемыми структурами данных, обладают соответствующим интерфейсом, обладают итераторами и могут быть обработаны циклом for - in. Метод iter, примененный к этим объектам, позволяет явно получить объект итератор, для которого доступны методы соответствующего интерфейса. Цикл for - in неявно создает итератор соответствующей структуры в процессе своей работы.
Приведу пример итерирования всех перечисленных структур данных:
#тесты с итерируемыми объектами, итераторами
L = ['L1', 'L2', 'L3']
T = ('T1', 'T2', 'T3')
D = {'k1' : 'd1', 'k2' : 'd2', 'k3' : 'd3'}
S = {'set1', 'set2', 'set3'}
Str = 'abcde'
def test1():
for item in L:
print(item, end = ', ')
print()
for item in T:
print(item, end = ', ')
print()
for item in D:
print(item, ': ', D[item], end = ', ')
print()
for item in S:
print(item, end = ', ')
print()
for item in Str:
print(item, end = ', ')
print()
F = open("Recs.txt")
for item in F:
print(item, end = '')
print()
F.close()
#Доступ по индексу
print(L[1]); print(T[1]); print(Str[1]);
#print(S[1]); print(D[1])
#Получение итератора встроенных структур
iter1 = iter(L); iter2 = iter(T); iter3 = iter(D);
iter4 = iter(S); iter5 = iter(Str);
F = open("Recs.txt")
iter6 = iter(F)
F.close()
print(iter1); print(iter2); print(iter3)
print(iter4); print(iter5); print(iter6)
Приведу результаты работы этого теста:
Как видите, цикл for item in X обрабатывает любую из рассмотренных структур данных. Для словарей итерирование идет по ключам словаря, для текстовых файлов итерирование выполняется по записям файла, выполняя по сути метод чтения записей readline.
Прежде чем дать определение итератора в языке Python , рассмотрим ряд функций, создающих итераторы и, следовательно, позволяющих обрабатывать данные в цикле for.
Хотя в языке Python нет классического цикла for, но он легко моделируется циклом for- in, используя метод range, создающий коллекцию целых чисел, играющую роль индексов классического цикла for. Фактически метод range создает итератор, а оператор for - in выполняет итерирование этой коллекции.
Приведу простой пример, в котором на печать выводятся элементы, стоящие на нечетных местах в упорядоченном списке:
def test2():
for i in range(len(L)):
if i % 2 == 0:
print(L[i], end = ', ')
print()
Результаты работы этого теста:
Не только функцию range, но и многие полезные функции Python можно рассматривать как итераторы. Функция map позволяет применить некоторую функцию, заданную в качестве первого аргумента, к итерируемым объектам, представляющих последующие аргументы функции map. Вот пример использования:
def test3():
L = [3, 5, 7, 9]
L1 = [2, 4, 8]
for item in map(lambda x, y : 2 * x + y, L, L1):
print(item, end = ', ')
print()
Результаты работы этого теста:
Функция map, как и у ряд других функций, представляющих итераторы, обладает несомненными достоинствами:
Давайте погрузимся в детали, связанные с функцией map. Приведу справку по классу map:
Поясню приведенную справку.
Класс map содержит функцию map, первым аргументом которой является функция в общем случае от n аргументов, следующие n аргументов - это итерируемые объекты, содержащие коллекции значений каждого из аргументов. Функция map создает итератор, который вычисляет значение функции, заданной в первом аргументе, используя значения каждого итерируемого объекта. Выполнение прекращается, когда исчерпан кратчайший итерируемый объект.
Важно заметить, что класс map реализует интерфейс, требуемый для итератора. Для этого необходимо и достаточно иметь два встроенных метода: __iter__ и __next__. Первый из этих методов возвращает ссылку на класс, позволяя создать объект итератор. Второй - выполняет итерирование, позволяя получить очередной элемент, создаваемый функцией map.
Подобно функции map устроены и другие функции, возвращающие итератор.
Функция zip позволяет создавать кортежи итерируемых объектов. В цикле можно задать некоторое выражение, вычисляемое на кортежах. Приведу пример использования функции zip для построения таблицы истинности логической функции:
def test5():
"""
таблица истинности логической функции
"""
L1 =[0, 0, 0, 0, 1, 1, 1 ,1]
L2 =[0, 0, 1, 1, 0, 0, 1 ,1]
L3 =[0, 1, 0, 1, 0, 1, 0 ,1]
print (list(zip(L1, L2, L3)))
for (x, y, z) in zip(L1, L2, L3):
print(' ', x and y or z, end = ' ' )
print()
Результаты работы этого теста:
Функция filter позволяет выбрать элементы из итерируемого объекта, используя фильтр, заданный логической функцией:
def test6():
r = range(30)
for number in filter(lambda x : x % 3 == 0, r):
print(number, end = ' ')
print()
Результаты работы этого теста:
Функция reduce, использует итератор для получения некоторой обобщенной характеристики Т итерируемого объекта, такой как сумма, произведение, максимальный или минимальный элемент. Для вычисления Т задается бинарная операция, которая выполняется в процессе итерирования над Т и очередным элементом. Начальное значение Т либо задается, либо по умолчанию используется значение первого элемента итерируемого объекта. Приведу пример вычисления максимального элемента, предполагая, что на элементах определена операция сравнения ">" (больше).
def test7():
from functools import reduce
t = (7, 12, 5, 11, 17, 6)
s = 'Уж небо осенью дышало'
n = reduce(max, t )
sym = reduce(max, s)
print ('максимальное число = ', n)
print( 'буква с максимальным кодом - ', sym)
Результаты работы этого теста:
Давайте попробуем более точно определить, что же такое итератор в языке Python. Вначале определим понятие итерируемого класса.
Итерируемый класс - это класс, реализация которого включает два метода - __iter__ и __next__. Эти два метода представляют интерфейс итератора. Пусть Т - объект итерируемого класса.
Итератор класса - это объект класса, который создается вызовом метода iter(T). Этот вызов эквивалентен вызову Т.__iter__().
Пусть It = iter(T) - объект итератор. Тогда вызов next(It), эквивалентный вызову It.__next__(), возвращает очередной элемент итерируемой коллекции и выбрасывает исключение StopIteration, если коллекция исчерпана.
Итерируемый класс позволяет создать итератор и, следовательно, организовать последовательный доступ к элементам, генерируемых при вызове метода __next__. Оператор цикла for-in позволяет работать с любым итератором.
Как мы видели, все встроенные типы данных являются итерируемыми классами и обладают соответствующими итераторами. Ряд встроенных функций - map и другие - являются функциями итерируемого класса и создают соответствующие итераторы.
Создавая собственный класс нетрудно сделать его итерируемым, для этого достаточно реализовать в классе интерфейс итератора - методы __iter__ и __next__.
Для того чтобы создаваемый класс был итерируемым необходимо и достаточно в этом классе реализовать интерфейс итератора. Метод __iter__ реализуется достаточно просто. Основная трудность обычно связана с реализацией метода __next__, который должен генерировать итерируемую последовательность. Конкретный алгоритм зависит от задачи, для решения которой создан класс. Возможны следующие схемы создания итерируемой последовательности:
map.Построим класс, следуя первой схеме. В классе создадим атрибут класса, общий для всех экземпляров, который содержит коллекцию имен. Атрибут экземпляра класса будет задавать индекс элемента в этой коллекции с нулевым значением по умолчанию. Метод __next__, используя эти атрибуты, будет выполнять итерирование по коллекции имен. Приведу реализацию этой идеи:
class Names(object):
"""
итератор, возвращающий имена из списка
"""
names = ('Анна', 'Борис', 'Виктор', 'Галина', 'Дмитрий')
def __init__(self):
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index < len(self.names):
name = self.names[self.index]
self.index +=1
return name
else:
raise StopIteration
Поскольку в классе не предполагается изменение коллекции имен, то атрибут names задан кортежем - неизменяемым типом данных. Атрибут index задается в конструкторе класса, получая по умолчанию значение ноль. Клиент класса не должен изменять значение этого атрибута. Метод интерфейса итератора __iter__ устроен совсем просто, - он возвращает ссылку на объект класса. Второй метод интерфейса __next__ возвращает имя из коллекции, используя атрибут index, одновременно увеличивая на единицу значение этого атрибута. Исключительная ситуация StopIteration возбуждается при исчерпании коллекции имен. Класс Names при таком определении является итерируемым классом. Построим тест, демонстрирующий возможность работы с объектами этого класса как итерируемыми объектами:
def test8():
from Names import Names
it = Names()
for name in it:
print(name, end = ' ')
print()
#it = Names()
for name in it:
print(name, end = ' ')
print()
Вот как выглядят результаты работы этого теста:
Важно заметить, что первое выполнение оператора цикла for выполняет итерирование и получает имена должным образом. Но второе выполнение этого оператора итерирование не выполняет и ничего не выводит на печать. Это ошибка?
Совсем нет. Так работает итератор в Python . При первом выполнении цикла for коллекция имен исчерпана, начальная ситуаций автоматически не восстанавливается, так что когда начинает выполняться второй раз оператор цикла for, то уже при первом вызове метода __next__ возникает исключительная ситуация StopIteration и цикл for прекращает работу. Чтобы повторно выполнить итерирование нужно создать итератор заново. Если раскомментровать оператор, повторно создающий объект класса, то все заработает должным образом:
Эту проблему можно решить и по-другому, изменив метод, создающий итератор. Давайте перенесем создание атрибута index из конструктора класса в метод __iter__. Тогда восстановление начального состояния итератора будет выполняться при создании итератора, так что клиенту не нужно будет повторно создавать объект, реанимируя итератор.
Приведу еще тест, в котором создаются два объекта класса и для каждого выполняется итерирование:
def test9():
from Names import Names
it = Names()
for name in it:
print(name, end = ' ')
print()это разные объекты
it1 = Names()
iter1 = iter(it1)
iter2 = iter(it1)
for name in iter1:
print(name, end = ' ')
print()
for name in iter2:
print(name, end = ' ')
print()
Заметьте, для второго объекта явно создаются объекты итераторы - iter1 и iter2. Это разные объекты или два имени одного объекта?
Давайте проанализируем результаты выполнения теста:
Имена it и it1 - это имена разных объектов и для каждого из них выполняется итерирование. Имена iter1 и iter2 - это имена одного объекта, поэтому, когда закончилось итерирование для итератора iter1, оно закончилось и для итератора iter2.
На этом закончу лекцию об итераторах, хотя разговор о них продолжится в следующей лекции, где будем рассматривать генераторы.
Проект для лекции Lecture10.rar.
Мы рассмотрели базисные элементы, составляющие основу программирования на языке Python. Мы понимаем как устроены типы данных языка, какие типы данных являются встроенными. Знаем синтаксис и семантику операторов языка, позволяющих написать программный код. Умеем, надеюсь, программировать в процедурах и функциях. Умеем использовать при построении программного проекта различные стили программирования, включая модульное и объектно-ориентированное программирование в классах и объектах. Понимаем, что при построении программного проекта необходимо обеспечить его корректность и устойчивость. Так что освоение курса "Основы программирования на языке Python" почти завершено.
Понимание основ требует рассмотрения деталей таких полезных и часто используемых инструментов, встроенных в язык, как итераторы, генераторы и декораторы.
Прежде чем попытаться понять, что такое итератор, давайте начнем с более простого понятия - итерация. В переводе с английского итерация - это повторение. Для программиста итерация связана с циклом. Каждый оператор цикла имеет заголовок и тело. Однократное выполнение тела цикла называется итерацией, а процесс выполнения цикла - итерированием. Как правило, в языках программирования встречается три типа цикла:
for, когда число итераций указано в заголовке и задан целочисленный параметр цикла (индекс), получающий на каждой итерации новое значение. Вот пример типичного заголовка цикла for в языках С++, C# - for(int I = 0; I < N; I++). У такого цикла N итераций и параметр I пробегает все значения от 0 до N - 1.While - создается тогда, когда число итераций не известно в начальной точке запуска цикла и не может быть задано в заголовке. Конечно, когда программист пишет цикл While, он предполагает (уверен), что цикл обязательно завершит свою работу.for each - цикл, обрабатывающий некоторую конечную структуру данных (контейнер данных). Число итераций определяется размером контейнера. На каждой итерации обрабатывается очередной элемент данных контейнера. Понятно, что данные контейнера должны быть перечислимы, то есть на данных задан некий порядок, известен начальный элемент и определена функция next (следующий), которая при первом обращении выдает первый по порядку элемент, а затем при каждом обращении выдает следующий элемент в заданном порядке. Предусмотрен также способ уведомления об исчерпании элементов контейнера. Структура данных, обладающая такими свойствами, называется итерируемой структурой данных.Понятие итерированной структуры данных можно обобщить на понятие итерируемого объекта произвольного класса. Так появляется понятие итератора. В Википедии дается следующее определение итератора:
Итератор (от англ. iterator ? перечислитель) - интерфейс, предоставляющий доступ к элементам коллекции (массива или контейнера) и навигацию по ним.
Не самое лучшее определение. Во-первых, в переводе итератор - это повторитель, (перечислитель в английском - это enumerator). Во-вторых, назвать итератор интерфейсом мало что проясняет, поскольку понятие интерфейс в данном контексте не определено. Итератор - это механизм, предоставляющий доступ к элементам коллекции. В разных языках этот механизм может быть различным и зависеть от типа коллекции. В языке Python итератор - это объект класса, обладающего соответствующим интерфейсом. В этой лекции мы подробно поговорим о том, каким именно интерфейсом должен обладать итерируемый класс, у которого существует объект итератор.
В языке Python есть два вида циклов - while и for - in. Цикл while соответствует общепринятому циклу этого вида. Цикл for - in является обобщенным вариантом цикла for - each и позволяет работать с любыми итерируемыми объектами, которые в Python используются гораздо чаще чем в других языках программирования.
Все структуры данных, наследуемые от последовательности, следовательно, перечислимые, имеющие доступ к элементам по индексу, являются итерируемыми по определению. Напомню, что к таким структурам относятся строки, списки, кортежи. Но ни файлы, ни множества, ни словари к таким структурам не относятся. Тем не менее, все перечисленные структуры являются итерируемыми структурами данных, обладают соответствующим интерфейсом, обладают итераторами и могут быть обработаны циклом for - in. Метод iter, примененный к этим объектам, позволяет явно получить объект итератор, для которого доступны методы соответствующего интерфейса. Цикл for - in неявно создает итератор соответствующей структуры в процессе своей работы.
Приведу пример итерирования всех перечисленных структур данных:
#тесты с итерируемыми объектами, итераторами
L = ['L1', 'L2', 'L3']
T = ('T1', 'T2', 'T3')
D = {'k1' : 'd1', 'k2' : 'd2', 'k3' : 'd3'}
S = {'set1', 'set2', 'set3'}
Str = 'abcde'
def test1():
for item in L:
print(item, end = ', ')
print()
for item in T:
print(item, end = ', ')
print()
for item in D:
print(item, ': ', D[item], end = ', ')
print()
for item in S:
print(item, end = ', ')
print()
for item in Str:
print(item, end = ', ')
print()
F = open("Recs.txt")
for item in F:
print(item, end = '')
print()
F.close()
#Доступ по индексу
print(L[1]); print(T[1]); print(Str[1]);
#print(S[1]); print(D[1])
#Получение итератора встроенных структур
iter1 = iter(L); iter2 = iter(T); iter3 = iter(D);
iter4 = iter(S); iter5 = iter(Str);
F = open("Recs.txt")
iter6 = iter(F)
F.close()
print(iter1); print(iter2); print(iter3)
print(iter4); print(iter5); print(iter6)
Приведу результаты работы этого теста:
Как видите, цикл for item in X обрабатывает любую из рассмотренных структур данных. Для словарей итерирование идет по ключам словаря, для текстовых файлов итерирование выполняется по записям файла, выполняя по сути метод чтения записей readline.
Прежде чем дать определение итератора в языке Python , рассмотрим ряд функций, создающих итераторы и, следовательно, позволяющих обрабатывать данные в цикле for.
Хотя в языке Python нет классического цикла for, но он легко моделируется циклом for- in, используя метод range, создающий коллекцию целых чисел, играющую роль индексов классического цикла for. Фактически метод range создает итератор, а оператор for - in выполняет итерирование этой коллекции.
Приведу простой пример, в котором на печать выводятся элементы, стоящие на нечетных местах в упорядоченном списке:
def test2():
for i in range(len(L)):
if i % 2 == 0:
print(L[i], end = ', ')
print()
Результаты работы этого теста:
Не только функцию range, но и многие полезные функции Python можно рассматривать как итераторы. Функция map позволяет применить некоторую функцию, заданную в качестве первого аргумента, к итерируемым объектам, представляющих последующие аргументы функции map. Вот пример использования:
def test3():
L = [3, 5, 7, 9]
L1 = [2, 4, 8]
for item in map(lambda x, y : 2 * x + y, L, L1):
print(item, end = ', ')
print()
Результаты работы этого теста:
Функция map, как и у ряд других функций, представляющих итераторы, обладает несомненными достоинствами:
Давайте погрузимся в детали, связанные с функцией map. Приведу справку по классу map:
Поясню приведенную справку.
Класс map содержит функцию map, первым аргументом которой является функция в общем случае от n аргументов, следующие n аргументов - это итерируемые объекты, содержащие коллекции значений каждого из аргументов. Функция map создает итератор, который вычисляет значение функции, заданной в первом аргументе, используя значения каждого итерируемого объекта. Выполнение прекращается, когда исчерпан кратчайший итерируемый объект.
Важно заметить, что класс map реализует интерфейс, требуемый для итератора. Для этого необходимо и достаточно иметь два встроенных метода: __iter__ и __next__. Первый из этих методов возвращает ссылку на класс, позволяя создать объект итератор. Второй - выполняет итерирование, позволяя получить очередной элемент, создаваемый функцией map.
Подобно функции map устроены и другие функции, возвращающие итератор.
Функция zip позволяет создавать кортежи итерируемых объектов. В цикле можно задать некоторое выражение, вычисляемое на кортежах. Приведу пример использования функции zip для построения таблицы истинности логической функции:
def test5():
"""
таблица истинности логической функции
"""
L1 =[0, 0, 0, 0, 1, 1, 1 ,1]
L2 =[0, 0, 1, 1, 0, 0, 1 ,1]
L3 =[0, 1, 0, 1, 0, 1, 0 ,1]
print (list(zip(L1, L2, L3)))
for (x, y, z) in zip(L1, L2, L3):
print(' ', x and y or z, end = ' ' )
print()
Результаты работы этого теста:
Функция filter позволяет выбрать элементы из итерируемого объекта, используя фильтр, заданный логической функцией:
def test6():
r = range(30)
for number in filter(lambda x : x % 3 == 0, r):
print(number, end = ' ')
print()
Результаты работы этого теста:
Функция reduce, использует итератор для получения некоторой обобщенной характеристики Т итерируемого объекта, такой как сумма, произведение, максимальный или минимальный элемент. Для вычисления Т задается бинарная операция, которая выполняется в процессе итерирования над Т и очередным элементом. Начальное значение Т либо задается, либо по умолчанию используется значение первого элемента итерируемого объекта. Приведу пример вычисления максимального элемента, предполагая, что на элементах определена операция сравнения ">" (больше).
def test7():
from functools import reduce
t = (7, 12, 5, 11, 17, 6)
s = 'Уж небо осенью дышало'
n = reduce(max, t )
sym = reduce(max, s)
print ('максимальное число = ', n)
print( 'буква с максимальным кодом - ', sym)
Результаты работы этого теста:
Давайте попробуем более точно определить, что же такое итератор в языке Python. Вначале определим понятие итерируемого класса.
Итерируемый класс - это класс, реализация которого включает два метода - __iter__ и __next__. Эти два метода представляют интерфейс итератора. Пусть Т - объект итерируемого класса.
Итератор класса - это объект класса, который создается вызовом метода iter(T). Этот вызов эквивалентен вызову Т.__iter__().
Пусть It = iter(T) - объект итератор. Тогда вызов next(It), эквивалентный вызову It.__next__(), возвращает очередной элемент итерируемой коллекции и выбрасывает исключение StopIteration, если коллекция исчерпана.
Итерируемый класс позволяет создать итератор и, следовательно, организовать последовательный доступ к элементам, генерируемых при вызове метода __next__. Оператор цикла for-in позволяет работать с любым итератором.
Как мы видели, все встроенные типы данных являются итерируемыми классами и обладают соответствующими итераторами. Ряд встроенных функций - map и другие - являются функциями итерируемого класса и создают соответствующие итераторы.
Создавая собственный класс нетрудно сделать его итерируемым, для этого достаточно реализовать в классе интерфейс итератора - методы __iter__ и __next__.
Для того чтобы создаваемый класс был итерируемым необходимо и достаточно в этом классе реализовать интерфейс итератора. Метод __iter__ реализуется достаточно просто. Основная трудность обычно связана с реализацией метода __next__, который должен генерировать итерируемую последовательность. Конкретный алгоритм зависит от задачи, для решения которой создан класс. Возможны следующие схемы создания итерируемой последовательности:
map.Построим класс, следуя первой схеме. В классе создадим атрибут класса, общий для всех экземпляров, который содержит коллекцию имен. Атрибут экземпляра класса будет задавать индекс элемента в этой коллекции с нулевым значением по умолчанию. Метод __next__, используя эти атрибуты, будет выполнять итерирование по коллекции имен. Приведу реализацию этой идеи:
class Names(object):
"""
итератор, возвращающий имена из списка
"""
names = ('Анна', 'Борис', 'Виктор', 'Галина', 'Дмитрий')
def __init__(self):
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index < len(self.names):
name = self.names[self.index]
self.index +=1
return name
else:
raise StopIteration
Поскольку в классе не предполагается изменение коллекции имен, то атрибут names задан кортежем - неизменяемым типом данных. Атрибут index задается в конструкторе класса, получая по умолчанию значение ноль. Клиент класса не должен изменять значение этого атрибута. Метод интерфейса итератора __iter__ устроен совсем просто, - он возвращает ссылку на объект класса. Второй метод интерфейса __next__ возвращает имя из коллекции, используя атрибут index, одновременно увеличивая на единицу значение этого атрибута. Исключительная ситуация StopIteration возбуждается при исчерпании коллекции имен. Класс Names при таком определении является итерируемым классом. Построим тест, демонстрирующий возможность работы с объектами этого класса как итерируемыми объектами:
def test8():
from Names import Names
it = Names()
for name in it:
print(name, end = ' ')
print()
#it = Names()
for name in it:
print(name, end = ' ')
print()
Вот как выглядят результаты работы этого теста:
Важно заметить, что первое выполнение оператора цикла for выполняет итерирование и получает имена должным образом. Но второе выполнение этого оператора итерирование не выполняет и ничего не выводит на печать. Это ошибка?
Совсем нет. Так работает итератор в Python . При первом выполнении цикла for коллекция имен исчерпана, начальная ситуаций автоматически не восстанавливается, так что когда начинает выполняться второй раз оператор цикла for, то уже при первом вызове метода __next__ возникает исключительная ситуация StopIteration и цикл for прекращает работу. Чтобы повторно выполнить итерирование нужно создать итератор заново. Если раскомментровать оператор, повторно создающий объект класса, то все заработает должным образом:
Эту проблему можно решить и по-другому, изменив метод, создающий итератор. Давайте перенесем создание атрибута index из конструктора класса в метод __iter__. Тогда восстановление начального состояния итератора будет выполняться при создании итератора, так что клиенту не нужно будет повторно создавать объект, реанимируя итератор.
Приведу еще тест, в котором создаются два объекта класса и для каждого выполняется итерирование:
def test9():
from Names import Names
it = Names()
for name in it:
print(name, end = ' ')
print()это разные объекты
it1 = Names()
iter1 = iter(it1)
iter2 = iter(it1)
for name in iter1:
print(name, end = ' ')
print()
for name in iter2:
print(name, end = ' ')
print()
Заметьте, для второго объекта явно создаются объекты итераторы - iter1 и iter2. Это разные объекты или два имени одного объекта?
Давайте проанализируем результаты выполнения теста:
Имена it и it1 - это имена разных объектов и для каждого из них выполняется итерирование. Имена iter1 и iter2 - это имена одного объекта, поэтому, когда закончилось итерирование для итератора iter1, оно закончилось и для итератора iter2.
На этом закончу лекцию об итераторах, хотя разговор о них продолжится в следующей лекции, где будем рассматривать генераторы.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.