17 октября 2012 г.

Понять итераторы


Итераторы, основы

Итераторы мы будем рассматривать на примере работы с файлами, как один из типов данных.
Например вызывая метод readline, мы перемещаемся к следующей строке:

>>> f = open(‘script1.py’) # Прочитать 4 строки из файла сценария 
>>> f.readline()           # Метод readline загружает одну строку
‘import sys\n’
>>> f.readline()
‘print(sys.path)\n’
>>> f.readline()
‘x = 2\n’
>>> f.readline()

‘print(2 ** 33)\n’
>>> f.readline()           # Вернет пустую строку по достижении конца файла
‘’
-----------------------------------------------------------------


Далее тот же самый пример, только используем функцию __next__().


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


>>> f = open(‘script1.py’) # Метод __next__ загружает одну строку
>>> f.__next__()# и возбуждает исключение по достижении конца
                # файла
‘import sys\n’
>>> f.__next__()
‘print(sys.path)\n’
>>> f.__next__()
‘x = 2\n’
>>> f.__next__()
‘print(2 ** 33)\n’
>>> f.__next__()
Traceback (most recent call last):
  ...текст сообщения об ошибке опущен...
StopIteration

Подобные объекты в языке Python считаются итерируемыми.
-----------------------------------------------------------------


Цикл for может автоматически вызывать метод __next__ для перемещения к следующей строке в каждой итерации.

Например, следующий фрагмент читает содержимое файла строку за строкой без явного обращения к методам файла:
>>> for line in open(‘script1.py’):# Использовать итератор файла
...     print(line.upper(), end=’’)   # Вызывает метод __next__, 
...                                   # перехватывает исключение StopIteration
IMPORT SYS
PRINT(SYS.PATH)
X = 2
PRINT(2 ** 33)
-----------------------------------------------------------------

Аналог, только используем метод readlines для загрузки содержимого файла в память в виде списка строк:
>>> for line in open(‘script1.py’).readlines():
...     print(line.upper(), end=’’)
...
IMPORT SYS
PRINT SYS.PATH
X = 2
PRINT 2 ** 33

Проигрышный способ, так как он не экономит память.
-----------------------------------------------------------------

Возможность  построчного  чтения  файлов с помощью цикла while:
>>> f = open(‘script1.py’)
>>> while True:
...     line = f.readline()
...     if not line: break
...     print(line.upper(), end=’’)
...
...вывод тот же самый...
Однако такой вариант наверняка будет работать медленнее версии, основанной на использовании итератора в цикле for, потому что итераторы внутри интерпретатора  выполняются  со  скоростью,  присущей  программам,  написанным на языке C, тогда как версия на базе цикла while работает со скоростью интерпретации байт-кода виртуальной машиной Python.
-----------------------------------------------------------------

Выполнение итераций вручную: iter и next:
В Python 3.0  имеется встроенная  функция nextкоторая автоматически вызывает метод __next__ объекта. Допустим, что у нас имеется итерируемый  объект X, тогда вызов next(X) будет равносилен вызову X.__next__(),  но выглядит  намного проще.
Пример далее покажет функцию в действии:

>>> f = open(‘script1.py’)
>>> f.__next__()       # Непосредственный вызов метода
‘import sys\n’
>>> f.__next__()
‘print(sys.path)\n’

Далее используем встроенную функцию next:

>>> f = open(‘script1.py’)
>>> next(f)            # Встроенная функция next вызовет метод __next__
‘import sys\n’
>>> next(f)
‘print(sys.path)\n’
-----------------------------------------------------------------

Обработка списков:

>>> L = [1, 2, 3]
>>> I = iter(L)        # Получить объект-итератор
>>> I.__next__()       # Вызвать __next__, чтобы перейти к 
                       # следующему элементу
1
>>> I.__next__()
2
>>> I.__next__()
3
>>> I.__next__()
Traceback (most recent call last):
  ...текст сообщения об ошибке опущен...
StopIteration
-----------------------------------------------------------------

Чтобы начать итерации по спискам, необходимо предварительно вызвать функцию iter:
>>> L = [1, 2, 3]
>>> iter(L) is L
False
>>> L.__next__()
AttributeError: ‘list’ object has no attribute ‘__next__’

>>> I = iter(L)
>>> I.__next__()
1
>>> next(I)            # То же, что и вызов метода I.__next__()
2
-----------------------------------------------------------------

Автоматический и ручной способ организации итераций:

L = [1, 2, 3]
>>>
>>> for X in L:                # Автоматический способ выполнения итераций
...     print(X ** 2, end=’ ‘) # Получает итератор, вызывает __next__, 
...                            # обрабатывает исключение
1 4 9


>>> I = iter(L)            # Ручной способ итераций: имитация цикла for
>>> while True:
...     try:     # Инструкция try обрабатывает исключения
...         X = next(I)    # Или I.__next__
...     except StopIteration:
...         break
...     print(X ** 2, end=’ ‘)
...
1 4 9
-----------------------------------------------------------------

Итераторы для словарей:

Инициализируем словарь:
>>> D = {‘a’:1, ‘b’:2, ‘c’:3}
>>> for key in D.keys():
...     print(key, D[key])
...
a 1
c 3
b 2

Делаем итерацию:

>>> I = iter(D)
>>> next(I)
‘a’
>>> next(I)
‘c’
>>> next(I)
‘b’
>>> next(I)
Traceback (most recent call last):
  ...текст сообщения об ошибке опущен...
StopIteration

-----------------------------------------------------------------

Цикл for автоматически извлекает ключи, по одному из каждой итерации:

>>> for key in D:
...     print(key, D[key])
...
a 1
c 3
b 2



Комментариев нет:

Отправить комментарий