Как создать словарь из строки текста? - PullRequest
8 голосов
/ 05 декабря 2010

У меня есть сгенерированный файл с тысячами строк, например:

CODE,XXX,DATE,20101201,TIME,070400,CONDITION_CODES,LTXT,PRICE,999.0000,QUANTITY,100,TSN,1510000001

В некоторых строках есть больше полей, а в других - меньше, но все они следуют одному и тому же шаблону пар ключ-значение, и каждая строка имеет поле TSN.

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

#!/usr/bin/env python

from sys import argv

records = {}
for line in open(argv[1]):
    fields = line.strip().split(',')
    record = dict(zip(fields[::2], fields[1::2]))
    records[record['TSN']] = record

print 'Found %d records in the file.' % len(records)

... это нормально и делает именно то, что я хочу (print - просто тривиальный пример).

Однако, это не кажется мне особенно "питоническим" и строка с:

dict(zip(fields[::2], fields[1::2]))

Который просто чувствует себя "неуклюжим" (сколько раз он повторяется по полям?).

Есть ли лучший способ сделать это в Python 2.6, используя только стандартные модули?

Ответы [ 4 ]

19 голосов
/ 05 декабря 2010

В Python 2 вы можете использовать izip в модуле itertools и магию объектов-генераторов, чтобы написать собственную функцию для упрощения создания пар значений для записей dict. Я получил идею для pairwise() из одноименного (но функционально другого) рецепта в Python 2 itertools docs.

Чтобы использовать подход в Python 3, вы можете просто использовать обычный zip(), поскольку он делает то, что izip() делал в Python 2, что приводит к удалению последнего из itertools & mdash; приведенный ниже пример решает эту проблему и должен работать в обеих версиях.

try:
    from itertools import izip
except ImportError:  # Python 3
    izip = zip

def pairwise(iterable):
    "s -> (s0,s1), (s2,s3), (s4, s5), ..."
    a = iter(iterable)
    return izip(a, a)

Что может быть использовано в вашем файле для чтения for loop:

from sys import argv

records = {}
for line in open(argv[1]):
    fields = (field.strip() for field in line.split(','))  # generator expr
    record = dict(pairwise(fields))
    records[record['TSN']] = record

print('Found %d records in the file.' % len(records))

Но подождите, это еще не все!

Можно создать обобщенную версию, которую я назову grouper(), которая снова соответствует одноименному, но функционально другому рецепту itertools (который указан ниже pairwise()):

def grouper(n, iterable):
    "s -> (s0,s1,...sn-1), (sn,sn+1,...s2n-1), (s2n,s2n+1,...s3n-1), ..."
    return izip(*[iter(iterable)]*n)

Что может быть использовано в вашем for цикле:

    record = dict(grouper(2, fields))

Конечно, для конкретных случаев, подобных этому, легко использовать functools.partial() и создавать с ним аналогичную функцию pairwise() (которая будет работать как в Python 2, так и в 3):

import functools
pairwise = functools.partial(grouper, 2)

Постскриптум

Если не существует действительно большого количества полей, вы можете вместо этого создать фактическую последовательность из пар строк (вместо использования выражения-генератора, у которого нет len()):

fields = tuple(field.strip() for field in line.split(','))

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

try:
    xrange
except NameError:  # Python 3
    xrange = range

def grouper(n, sequence):
    for i in xrange(0, len(sequence), n):
        yield sequence[i:i+n]

pairwise = functools.partial(grouper, 2)
6 голосов
/ 05 декабря 2010

Не намного лучше, чем просто более эффективно ...

Полное объяснение

2 голосов
/ 05 декабря 2010
import itertools

def grouper(n, iterable, fillvalue=None):
    "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return itertools.izip_longest(fillvalue=fillvalue, *args)

record = dict(grouper(2, line.strip().split(","))

источник

1 голос
/ 05 декабря 2010

Если мы все равно собираемся абстрагировать ее в функцию, написать «с нуля» несложно:

def pairs(iterable):
    iterator = iter(iterable)
    while True:
        try: yield (iterator.next(), iterator.next())
        except: return

Версия рецепта Роберта определенно выигрывает очки за гибкость.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...