20 октября 2012 г.

Итерируемые объекты


Новые итерируемые объекты

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


>>> zip(‘abc’, ‘xyz’) # Итерируемый объект в Python 3.0 (список в 2.6)
<zip object at 0x02E66710>

>>> list(zip(‘abc’, ‘xyz’))          # Принудительное создание списка 
[(‘a’, ‘x’), (‘b’, ‘y’), (‘c’, ‘z’)] # результатов для отображения в 3.0
-----------------------------------------------------------------


Итератор range:

В версии 3.0 функция range возвращает итератор, который не создает сразу весь список целых чисел в заданном диапазоне, а генерирует их по требованию. Она действует точно так же, как прежняя функция xrange в версии 2.X и в случае, когда необходимо получить сразу весь список с результатами (например, для отображения), обращение к ней следует обернуть в вызов функции list(range(...)):

C:\\misc> c:\python30\python
>>> R = range(10)  # range возвращает итератор, а не список
>>> R
range(0, 10)

>>> I = iter(R)    # Вернет итератор для диапазона
>>> next(I)        # Переход к следующему результату
0     # То же происходит в циклах for, генераторах списков и пр. 
>>> next(I)
1
>>> next(I)
2
>>> list(range(10))   # При необходимости можно принудительно 
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] # сгенерировать список
-----------------------------------------------------------------

Итераторы  map,zip и filter:
Встроенные функции map, zip и filter возвращают итераторы вместо того, чтобы воспроизводить сразу весь список с результатами.

Однако, в отличие от функции range, их результаты сами и являются этими итераторами, – после однократного получения отдельного результата этот результат исчезает.

Пример использования встроенной функции map:
>>> M = map(abs, (-1, 0, 1))   # map возвращает итератор, а не список
>>> M
<map object at 0x0276B890>
>>> next(M) # Непосредственное использование итератора: 
1          # результаты исчерпываются безвозвратно. 
>>> next(M)                    # Они не поддерживают функцию len() 
0                              # и операцию индексирования
>>> next(M)
1
>>> next(M)
StopIteration
>>> for x in M: print(x)       # Теперь итератор map пуст: 
...                            # возможен только один проход
>>> M = map(abs, (-1, 0, 1))   # Чтобы выполнить второй проход,  
                               # необходимо 
                               # снова создать итератор
>>> for x in M: print(x)       # В контексте итераций функция next()
...                            # вызывается автоматически
1
0
1
>>> list(map(abs, (-1, 0, 1))) # При необходимости можно получить
                               # сразу 
[1, 0, 1]                      # весь список с результатами
-----------------------------------------------------------------

Пример встроенной функции zip:
>>> Z = zip((1, 2, 3), (10, 20, 30)) # zip также возвращает итератор, который 
>>> Z           # позволяет выполнить только один проход
<zip object at 0x02770EE0>
>>> list(Z)
[(1, 10), (2, 20), (3, 30)]
>>> for pair in Z: print(pair)       # Результаты исчерпываются после 
...                                  # первого прохода
>>> Z = zip((1, 2, 3), (10, 20, 30))
>>> for pair in Z: print(pair)       # Итератор можно использовать 
...                                  # вручную или автоматически
(1, 10)
(2, 20)
(3, 30)
>>> Z = zip((1, 2, 3), (10, 20, 30))
>>> next(Z)
(1, 10)
>>> next(Z)
(2, 20)
-----------------------------------------------------------------

Поддержка множественных и единственных итераторов:
Возможность применение к результату несколько итераторов.
>>> R = range(3)   # Объект диапазона позволяет получить множество итераторов
>>> next(R)
TypeError: range object is not an iterator
>>> I1 = iter(R)
>>> next(I1)
0
>>> next(I1)
1
>>> I2 = iter(R)   # Два итератора для одного диапазона
>>> next(I2)
0
>>> next(I1)      # I1 находится в другой позиции, не совпадающей с позицией I2
2

Функции zip, map и filter, напротив, не поддерживают возможность получения 
различных активных итераторов для одного и того же результата:
>>> Z = zip((1, 2, 3), (10, 11, 12))
>>> I1 = iter(Z)
>>> I2 = iter(Z)  # Два итератора для одного и того же результата  
                  # zip
>>> next(I1)
(1, 10)
>>> next(I1)
(2, 11)
>>> next(I2)           # Позиции итераторов I2 и I1 совпадают!
(3, 12)
>>> M = map(abs, (-1, 0, 1)) # То же относится к функции map (и filter)
>>> I1 = iter(M); I2 = iter(M)
>>> print(next(I1), next(I1), next(I1))
1 0 1
>>> next(I2)
StopIteration
>>> R = range(3)               # А для объекта диапазона можно получить 
>>> I1, I2 = iter(R), iter(R)  # множество итераторов
>>> [next(I1), next(I1), next(I1)]
[0 1 2]
>>> next(I2)
0
-----------------------------------------------------------------

Итераторы представлений словарей:
Методы keys, values и items слова
рей возвращают  итерируемые  объекты  представлений, которые возвращают 
результаты по одному за раз  вместо того, чтобы сразу создавать списки с ре
зультатами. 
>>> D = dict(a=1, b=2, c=3)
>>> D
{‘a’: 1, ‘c’: 3, ‘b’: 2}
>>> K = D.keys()   # Объект представления в версии 3.0 не является списком
>>> K
<dict_keys object at 0x026D83C0>
>>> next(K)        # Представления не являются итераторами
TypeError: dict_keys object is not an iterator
>>> I = iter(K)    # Из представлений можно получить итераторы
>>> next(I)        # и с их помощью выполнять итерации вручную,
‘a’                # но они не поддерживают функцию len()
>>> next(I)        # и операцию доступа к элементам по индексу
‘c’
>>> for k in D.keys(): print(k, end=’ ‘) # Во всех итерационных контекстах
...                                      # итераторы используются 
a c b                                    # автоматически
-----------------------------------------------------------------

Доступ по индексу:
>>> K = D.keys()
>>> list(K)    # При необходимости всегда можно получить полный список
[‘a’, ‘c’, ‘b’]
>>> V = D.values() # То же относится к представлениям values() и items()
>>> V
<dict_values object at 0x026D8260>
>>> list(V)
[1, 3, 2]
>>> list(D.items())
[(‘a’, 1), (‘c’, 3), (‘b’, 2)]
>>> for (k, v) in D.items(): print(k, v, end=’ ‘)
...
a 1 c 3 b 2
-----------------------------------------------------------------

Пример когда  нет никакой необходимости вызывать метод keys:

>>> D                    # Словари поддерживают собственные итераторы,
{‘a’: 1, ‘c’: 3, ‘b’: 2} # возвращающие следующий ключ в каждой итерации
>>> I = iter(D)
>>> next(I)
‘a’
>>> next(I)
‘c’
>>> for key in D: print(key, end=’ ‘) # Нет никакой необходимости 
                                      # вызывать
...                                   # метод keys(), однако этот
                                      # метод 
a c b                                 # в версии 3.0 также 
                                      # возвращает итератор!
----------------------------------------------------------------

И на последок:
>>> D
{‘a’: 1, ‘c’: 3, ‘b’: 2}
>>> for k in sorted(D.keys())): print(k, D[k], end=’ ‘)
...
a 1 b 2 c 3

>>> D
{‘a’: 1, ‘c’: 3, ‘b’: 2}
>>> for k in sorted(D): print(k, D[k], end=’ ‘) # Лучший способ 
...                                     # сортировки ключей
a 1 b 2 c 3

Добавим теории:
  • С помощью  инструкции  yield  пользовательские  функции  можно  превратить в итерируемые функции-генераторы.
  • Генераторы списков можно трансформировать в итерируемые выражения генераторы, заключив их в круглые скобки.
  • В пользовательские  классы можно добавить поддержку итераций с помощью методов перегрузки операторов __iter__ или __getitem__.

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

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