一尘不染

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

python

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

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中有更好的方法吗?


阅读 176

收藏
2020-12-20

共1个答案

一尘不染

在Python
2中,您可以izipitertools模块和生成器对象中使用魔术来编写自己的函数,以简化dict记录值对的创建。我pairwise()从Python
2文档中一个类似命名(但功能不同)的食谱中得到了这个想法itertools

要在Python 3中使用该方法,您可以使用plain,zip()因为它的作用与izip()在Python 2中的作用相同,导致将后者从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)
2020-12-20