如何从文本行创建字典?

10 投票
4 回答
5543 浏览
提问于 2025-04-16 08:02

我有一个生成的文件,里面有成千上万行数据,格式大概是这样的:

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 个回答

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(","))

来源

6

并不是说更好,而是说更高效...

完整的解释

27

在Python 2中,你可以使用izip这个来自itertools模块的工具,以及生成器对象的特性,来写一个自己的函数,方便地创建值对,用于dict记录。我从Python 2的itertools文档中一个名字相似(但功能不同)的示例得到了pairwise()这个想法。

在Python 3中,你可以直接使用普通的zip(),因为它的功能和Python 2中的izip()是一样的,所以后者在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)

撰写回答