将字典列表转换为命名元组列表的Pythonic方式

7

我有一个由 dict 组成的 list,需要将其转换为以空格分隔第一个变量的 namedtuple(首选)或简单的 tuplelist

有更加Pythonic的方法吗?

我稍微简化了我的代码。欢迎使用推导式、生成器表达式和 itertools。

数据输入:

dl = [{'a': '1 2 3',
       'd': '*',
       'n': 'first'},
      {'a': '4 5',
       'd': '*', 'n':
       'second'},
      {'a': '6',
       'd': '*',
       'n': 'third'},
      {'a': '7 8 9 10',
       'd': '*',
       'n': 'forth'}]

简单算法:

from collections import namedtuple

some = namedtuple('some', ['a', 'd', 'n'])

items = []
for m in dl:
    a, d, n = m.values()
    a = a.split()
    items.append(some(a, d, n))

输出:

[some(a=['1', '2', '3'], d='*', n='first'),
 some(a=['4', '5'], d='*', n='second'),
 some(a=['6'], d='*', n='third'),
 some(a=['7', '8', '9', '10'], d='*', n='forth')]
4个回答

7
以下是@Petr Viktorin对我的原始答案和您的初始解决方案提出的问题:
警告!字典的值(values())没有特定的顺序!如果此解决方案有效,并且确实以a,d,n的顺序返回,那么这只是巧合。如果您使用不同版本的Python或以不同的方式创建字典,则可能会出现错误。
(我有点惊讶我一开始没有注意到这个问题并因此获得了45个声望!)
请改用@eryksun的建议:
items =  [some(m['a'].split(), m['d'], m['n']) for m in dl]

我的原始、不正确的答案。除非你有一个OrderedDict列表,否则不要使用它。

items =  [some(a.split(), d, n) for a,d,n in (m.values() for m in dl)]

2
警告!字典的values()没有特定的顺序!如果这个解决方案有效,并且a, d, n确实按照那个顺序返回,那只是巧合。如果你使用不同版本的Python或以不同的方式创建字典,可能会出现问题。直接使用键,就像@eryksun的评论中所说的那样。 - Petr Viktorin
@PetrViktorin - 哇,我简直不敢相信我错过了那个。感谢您的纠正! - detly
@PetrViktorin 我怎么会忘记那个...我会使用 OrderedDict 代替。 - scraplesh
2
我发现 namedtuple('xxx', d.keys())(**d) 非常方便。 - Alexander Oh

3

因为我非常喜欢namedtuples和dictionaries,所以我想在这里参与一下讨论!

以下是一个包含字典推导式的列表推导式,可以对字典进行初始处理:

split_dictionaries = \ 
    [{key: value.split() for k, value in d.iteritems()} for d in dl] 

我经常使用一种称为“tupperware”的配方,它可以递归地将字典转换为命名元组。代码请参考这里的gist。下面是一个简化版本的代码,可以用来执行此操作并以简洁的方式进行处理。
import collections

def namedtuple_from_mapping(mapping, name="Tupperware"):
    this_namedtuple_maker = collections.namedtuple(name, mapping.iterkeys())
    return this_namedtuple_maker(**mapping)

假设有了这个函数,你可以这样做 - 我们很快会对其进行重构:

split_namedtuples = [ 
    namedtuple_from_mapping(
        {key: value.split() for k, value in d.iteritems()}
    ) for d in dl
]   

现在,通过更好的封装和可读性:

def format_string(string):
    return string.split()

def format_dict(d):
    return {key: format_string(value) for key, value in d.iteritems()}

formatted_namedtuples = [namedtuple_from_mapping(format_dict(d)) for d in dl]

2

另一种选择,不确定它是比其他选择更好还是更差:

class some(namedtuple('some', 'a d n')):
    def __new__(cls, **args):
        args['a'] = args['a'].split()
        return super(some, cls).__new__(cls, **args)

items = list(some(**m) for m in dl)

顺便说一句,我并不是非常坚持将基类和子类都命名为some。我喜欢它是因为这意味着产生的类使用名称some进行字符串转换,而且它从未给我带来过问题,但如果您在使用类名调试时可能会造成混淆,所以请谨慎使用。

或者使用不同的技巧表达相同的思路:

some = namedtuple('some', 'a d n')

def make_some(args):
    args = args.copy()
    args['a'] = args['a'].split()
    return some(**args)

items = map(make_some, dl) # NB: this doesn't return a list in Python 3

2

除了@detly提供的答案之外,如果你事先不知道字典的字段,可以构造一个namedtuple类。

some = namedtuple('some', set(k for k in d.keys() for d in dl))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接