Skip to content

Python: трюки с генераторами Часть 2 Обработка файлов данных

Dmitry Ponyatov edited this page Oct 4, 2019 · 8 revisions

Python: трюки с генераторами Часть 2 Обработка файлов данных

(Покажите мне логи своего веб-сервера)

Проблема программирования


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


81.107.39.38 - ... "GET /ply/ HTTP/1.1" 200 7587
81.107.39.38 - ... "GET /favicon.ico HTTP/1.1" 404 133
81.107.39.38 - ... "GET /ply/bookplug.gif HTTP/1.1" 200 23903
81.107.39.38 - ... "GET /ply/ply.html HTTP/1.1" 200 97238
81.107.39.38 - ... "GET /ply/example.html HTTP/1.1" 200 2359
66.249.72.134 - ... "GET /index.html HTTP/1.1" 200 4447

Ах да, и файл журнала может быть огромным (десятки гигабайт)

Файл журнала

  • Каждая строка журнала выглядит так:
    81.107.39.38 - ... "GET /ply/ply.html HTTP/1.1" 200 97238
  • Количество байт -- последний столбец
    bytes_sent = line.rsplit(None,1)[1]
  • Это либо число, либо пропущенное значение (-)
    81.107.39.38 - ... "GET /ply/ HTTP/1.1" 304 -
  • Преобразование значения
if bytes_sent != '-':
    bytes_sent = int(bytes_sent)

Не-генераторное решение

  • Просто используйте простой цикл for
with open("access-log") as wwwlog:
    total = 0
    for line in wwwlog:
        bytes_sent = line.rsplit(None,1)[1]
        if bytes_sent != '-':
            total += int(bytes_sent)
    print("Total", total)
  • Мы читаем построчно и просто обновляем сумму
  • Однако, это стиль 90х...

Генераторное решение

  • Давайте используем несколько генераторных выражений
with open("access-log") as wwwlog:
    bytecolumn = (line.rsplit(None,1)[1] for line in wwwlog)
    bytes_sent = (int(x) for x in bytecolumn if x != '-')
    print("Total", sum(bytes_sent))
  • Вау! Это другое!
    • Меньше кода
    • Совершенно другой стиль программирования

Генераторы как трубопровод

  • Чтобы понять решение, подумайте о нем как о конвейере обработки данных
журнал -> wwwlog -> bytecolumn -> bytes_sent -> sum() -> total
  • Каждый шаг определяется итерацией / генерацией

Быть декларативным

  • На каждом шаге конвейера мы объявляем операцию, которая будет применяться ко всему входному потоку.
    line.rsplit(None,1)[1] эта операция применяется к каждой строке файла журнала
  • Вместо того, чтобы фокусироваться на проблеме на построчном уровне, вы просто разбиваете ее на большие потоковые операции, которые работают сразу со всем файлом
  • Это «декларативный» стиль
  • Ключ: мыслить масштабно ...

Итерация это клей

  • Клей, который удерживает конвейер вместе -- это итерация, которая происходит на каждом этапе
    open("access-log") | for line in wwwlog | for x in bytecolumn | sum(bytes_sent)
  • Вычисления управляются последним шагом
  • Функция sum() потребляет значения, передаваемые по конвейеру (через вызовы __next __())

Производительность

  • Конечно, этот генераторный подход имеет все виды причудливой магии, которая кажется медленной.
  • Давайте проверим это на лог-файле 1.3Gb ...
% ls -l big-access-log
-rw-r--r-- beazley 1303238000 Feb 29 08:06 big-access-log
  • классический цикл -- 18.6с
  • генераторы -- 16.7c

Комментарий

  • Это было не просто не медленно, но и на 10% быстрее
  • Меньше кода
  • который относительно легко читать
  • И, честно говоря, мне так больше нравится ...

«В старые добрые времена мы использовали AWK для этого, и нам нравилось. О, да, и валите с моего газона!»

  • генераторы -- 16.7c
  • AWK -- 70.5c (!)
% awk '{ total += $NF } END { print total }' big-access-log

Примечание: извлечение последнего столбца не сильная сторона awk

Пицца для размышлений

  • Ни в одном из наших генераторных решений мы никогда не создавали большие вр_е_менные списки.
  • Таким образом, это решение не только быстрее, но и может применяться к огромным файлам данных.
  • Это более чем конкурентоспособно по сравнению с традиционными инструментами

Больше мыслей

  • Генераторное решение было основано на концепции конвейерной передачи данных между различными компонентами.
  • Что если у вас есть более продвинутые компоненты для работы?
  • Возможно, вы могли бы выполнить различные виды обработки, просто соединяя различные компоненты конвейера

Это звучит знакомо

  • Философия Unix
    • Есть коллекция полезных системных утилит
    • Можно подключить их к файлам или друг другу
    • Выполняйте сложные задачи, передавая данные через потоки

Python: трюки с генераторами Часть 3 Игры с файлами и каталогами

Clone this wiki locally