-
Notifications
You must be signed in to change notification settings - Fork 5
Python: трюки с генераторами Часть 2 Обработка файлов данных
Dmitry Ponyatov edited this page Oct 4, 2019
·
8 revisions
Узнайте, сколько байт данных было передано, путем суммирования последнего столбца данных в этом журнале веб-сервера 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 Игры с файлами и каталогами