Шаг 77.
Python: сборник рецептов.
Итераторы и генераторы. Замена бесконечных циклов while итератором

    На этом шаге мы рассмотрим малоизвестную возможность встроенной функции iter().

Задача

    У вас есть код, который использует цикл while для итеративной обработки данных, потому что в программе присутствует функция или какое-то необычное проверочное условие, которое нельзя вместить в стандартный итерационный паттерн.

Решение

    Вполне обычный код для программ, работающих с вводом-выводом:

CHUNKSIZE = 8192

def reader(s):
    while True:
        data = s.recv(CHUNKSIZE)
        if data == b'':
            break
        process_data(data)

    Такой код часто можно заменить использованием iter(), как показано ниже:

def reader(s):
    for chunk in iter(lambda: s.recv(CHUNKSIZE), b''): 
        process_data(data)

    Если вы сомневаетесь, будет ли это работать, то можете попробовать похожий пример для обработки файлов:

>>> import sys
>>> f = open('/etc/passwd')
>>> for chunk in iter(lambda: f.read(10), b''):
            n = sys.stdout.write(chunk)

.  .  .
nobody:*:-2:-2:Unprivileged User:/var/empty:/usr/bin/false
root:*:0:0:System Administrator:/var/root:/bin/sh
daemon:*:1:1:System Services:/var/root:/usr/bin/false
_uucp:*:4:4:Unix to Unix Copy Protocol:/var/spool/uucp:/usr/sbin/uucico
.  .  .
>>>


Обсуждение

    Малоизвестная возможность встроенной функции iter() заключается в том, что она может опционально принимать вызываемый (callable) аргумент и пороговое значение. При таком использовании функция создает итератор, который снова и снова повторяет вызов предоставленного вызываемого объекта, пока он не вернет значение, равное пороговому значению.

    Этот конкретный подход хорошо работает с некоторыми типами многократно вызываемых функций, таких как операции ввода-вывода. Например, если вы хотите читать данные кусочками ("chunk") из файлов или сокетов, то обычно должны многократно вызывать read() или recv() с последующей проверкой достижения конца файла. Представленный выше рецепт просто берет эти две функциональности и совмещает в единственном вызове iter(). Использование lambda в решении необходимо для создания вызываемого объекта, который не принимает аргументов, но при этом поставляет аргумент нужного размера в recv() или read().

    Со следующего шага мы начнем рассмотривать файлы и ввод-вывод.




Предыдущий шаг Содержание