Данные и итерация — Примеры

Пример 1 — Скан, спрятанный внутри цикла

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

subscribers = read_subscribers()      # список словарей
allowed = []
for user in read_users():
    for sub in subscribers:
        if sub["email"] == user["email"]:
            allowed.append(user)

Решение

subscribers = read_subscribers()
by_email = {sub["email"] for sub in subscribers}

allowed = [user for user in read_users() if user["email"] in by_email]

Проверка вхождения в множестве занимает фактически константное время, поэтому каждый пользователь получает ответ за одну проверку вместо прохода по всему списку подписчиков, а сами пользователи по-прежнему читаются за один проход. Ошибка здесь — оставить вложенный цикл и подкручивать его; однако стоимость решает коллекция, а не цикл. Когда нужен ответ-значение, а не «да или нет», берите словарь.

Пример 2 — Обработка файла, который не влезает в память

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

def totals(path):
    with open(path) as handle:
        numbers = [int(line) for line in handle]
    return sum(numbers)

Решение

def lines(path):
    with open(path) as handle:
        for line in handle:
            yield line.strip()

def totals(path):
    return sum(int(line) for line in lines(path) if line)

Каждое значение выдаётся тогда, когда его запрашивают, поэтому конвейер держит только текущую строку и работает с файлом больше памяти, а сам файл всё равно закрывается, когда генератор исчерпан. Ошибка — вернуть list(...) где-то в середине конвейера: это кладёт весь файл обратно в память и возвращает исходный сбой. Генератор выручает и тогда, когда нужен только первый подходящий элемент, а остальные были бы отброшены.