Skip to content

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

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

Python: трюки с генераторами

Часть 3 Игры с файлами и каталогами

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


У вас есть сотни логов веб-сервера, разбросанных по разным каталогам. Кроме того, некоторые журналы сжаты. Измените последнюю программу так, чтобы вы могли легко прочитать все эти журналы


foo/
    access-log-012007.gz
    access-log-022007.gz
    access-log-032007.gz
    ...
    access-log-012008
bar/
    access-log-092007.bz2
    ...
    access-log-022008

Path.rglob()

  • Полезный способ поиска в файловой системе
from pathlib import Path

for filename in Path('/').rglob('*.py'):
    print(filename)
  • Угадай, что? Он использует генераторы!
>>> from pathlib import Path
>>> Path('/').rglob('*.py')
<generator object Path.rglob at 0x10e3e0b88>
>>>
  • Таким образом, вы можете построить из него конвейеры обработки

Открывашка для файлов

  • Откройте последовательность путей
import gzip, bz2
def gen_open(paths):
    for path in paths:
        if path.suffix == '.gz':
            yield gzip.open(path, 'rt')
        elif path.suffix == '.bz2':
            yield bz2.open(path, 'rt')
        else:
            yield open(path, 'rt')
  • Это интересно .... он принимает последовательность путей в качестве входных данных и выдает последовательность открытых файловых объектов

cat

  • Объединить элементы из одного или нескольких источников в одну последовательность элементов
def gen_cat(sources):
    for src in sources:
        for item in src:
            yield item

или c новым синтаксисом yield from

def gen_cat(sources):
    for src in sources:
        yield from src
  • Пример:
lognames = Path('/usr/www').rglob("access-log*")
logfiles = gen_open(lognames)
loglines = gen_cat(logfiles)

Дополнение: yield from

  • yield from может использоваться для делегирования итерации из вложенных циклов for/yield
def countup(stop):
    n = 1
    while n < stop:
        yield n
        n += 1
def countdown(n):
    while n > 0:
        yield n
        n -= 1
def up_and_down(n):
    yield from countup(n)
    yield from countdown(n)
>>> for x in up_and_down(3):
... print(x)
...
1
2
3
2
1
>>>

grep

  • Генерация последовательности строк, содержащих заданное регулярное выражение
import re
def gen_grep(pat, lines):
    patc = re.compile(pat)
    return (line for line in lines if patc.search(line))
  • Пример:
lognames = Path('/usr/www').rglob("access-log*")
logfiles = gen_open(lognames)
loglines = gen_cat(logfiles)
patlines = gen_grep(pat, loglines)

Пример

  • Узнайте, сколько байт передано для определенного шаблона в целом каталоге журналов
pat        = r"somepattern"
logdir     = "/some/dir/"

filenames  = Path(logdir).rglob("access-log*")
logfiles   = gen_open(filenames)
loglines   = gen_cat(logfiles)
patlines   = gen_grep(pat,loglines)
bytecolumn = (line.rsplit(None,1)[1] for line in patlines)
bytes_sent = (int(x) for x in bytecolumn if x != '-')

print("Total", sum(bytes_sent))

Важная концепция

  • Генераторы отделяют итерацию от кода, который использует результаты итерации
  • В последнем примере мы выполняем вычисление для последовательности строк
    • Неважно, где и как эти строки генерируются
    • Таким образом, мы можем подключить любое количество компонентов заранее, если они в конечном итоге создают последовательность строк

Python: трюки с генераторами Часть 4 Синтаксический разбор и обработка данных

Clone this wiki locally