如何从文本行创建字典?
我有一个生成的文件,里面有成千上万行数据,格式大概是这样的:
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 个回答
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(","))
并不是说更好,而是说更高效...
在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)