Как создать словарь из текста?
у меня есть сгенерированный файл с тысячами строк вроде следующего:
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 ответов
в Python 2 вы можете использовать izip
на itertools
модуль и магия объектов генератора, чтобы написать свою собственную функцию, чтобы упростить создание пар значений для dict
записей. У меня есть идея для pairwise()
от аналогично названного (но функционально отличающегося) рецепт в Python 2 itertools
документы.
чтобы использовать подход в Python 3, Вы можете просто использовать plain zip()
С izip()
сделал в Python 2, в результате чего последний удаление из itertools
- пример ниже обращается к этому и должен работать в обеих версиях.
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
петли:
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)
Postscript
если нет действительно огромного количества полей, вы можете вместо этого создать фактическую последовательность из пар элементов строки (а не использовать выражение генератора, которое не имеет len()
):
fields = tuple(field.strip() for field in line.split(','))
вы могли бы обойтись и проще grouper()
функция:
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)
не намного лучше, чем просто более эффективным...
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(","))
Если мы собираемся абстрагировать его в функцию в любом случае, не слишком сложно написать "с нуля":
def pairs(iterable):
iterator = iter(iterable)
while True:
try: yield (iterator.next(), iterator.next())
except: return
версия рецепта Роберта определенно выигрывает очки за гибкость, хотя.