如何从一行文字创建字典?

时间:2010-12-04 23:06:46

标签: python parsing dictionary

我有一个包含数千行的生成文件,如下所示:

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只是一个简单的例子。)

然而,它并没有让我觉得特别“pythonic”和以下行:

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

只是感觉“笨拙”(它在场上迭代了多少次?)。

有没有更好的方法在Python 2.6中使用标准模块进行此操作?

4 个答案:

答案 0 :(得分:19)

在Python 2中,你可以在izip模块中使用itertools和生成器对象的魔力来编写自己的函数,以简化dict记录的值对的创建。我从Python 2 pairwise()文档中的同名(但功能不同)recipe获得了itertools的想法。

要在Python 3中使用该方法,您可以使用普通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)

<强>后记

除非有非常多的字段,否则您可以在线对项对中创建实际序列(而不是使用没有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)

答案 1 :(得分:6)

答案 2 :(得分: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(","))

source

答案 3 :(得分:1)

如果我们要将它抽象成一个函数,那么“从头开始”写起来并不难:

def pairs(iterable):
    iterator = iter(iterable)
    while True:
        try: yield (iterator.next(), iterator.next())
        except: return
不过,罗伯特的食谱版本无疑会获得灵活性。