将有序列表转换为字典的Pythonic方法

1 投票
6 回答
2316 浏览
提问于 2025-04-15 15:28

我似乎找不到一个优雅的方法,从 t 开始,最终得到 s

>>>t = ['a',2,'b',3,'c',4]
#magic
>>>print s
{'a': 2, 'c': 4, 'b': 3}

我想到的一些解决方案,看起来都不太优雅:

s = dict()
for i in xrange(0, len(t),2): s[t[i]]=t[i+1]
# or something fancy with slices that I haven't figured out yet

这个问题显然是可以轻松解决的,但我觉得似乎还有更好的方法。有没有呢?

6 个回答

7

这段代码效率可能不是很高,但如果你处理的列表不大,那就没关系:

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

或者你可以用生成器来写你的版本:

dict(t[i:i+2] for i in xrange(0, len(t), 2))
9

这个方法和Lukáš Lalinský的回答是同样的思路,只是用的方式不同:

>>> dict(zip(*([iter(t)] * 2)))
{'a': 2, 'c': 4, 'b': 3}

这里使用了dictzipiter这几个函数。它比Lukáš的回答更好的地方在于,它适用于任何可迭代的对象。下面是它的工作原理:

  1. iter(t)会创建一个针对列表t的迭代器。
  2. [iter(t)] * 2会创建一个包含两个元素的列表,这两个元素都指向同一个迭代器。
  3. zip是一个函数,它可以接收两个可迭代对象,并将它们的元素配对:第一个元素配对在一起,第二个元素配对在一起,依此类推,直到其中一个可迭代对象用完。
  4. zip(*([iter(t)] * 2))会将同一个迭代器作为两个参数传给zip。这样,zip就会将t的第一个和第二个元素配对,然后是第三个和第四个,接着是第五个和第六个,依此类推。
  5. dict会接收一个包含(key, value)对的可迭代对象,并根据这些对创建一个字典。
  6. dict(zip(*([iter(t)] * 2)))就按照提问者的要求创建了字典。
10

我会使用 itertools,不过如果你觉得那太复杂(正如你在评论中提到的),那么也许可以试试:

def twobytwo(t):
  it = iter(t)
  for x in it:
    yield x, next(it)

d = dict(twobytwo(t))

或者换个说法,再回到 itertools:

def twobytwo(t):
  a, b = itertools.tee(iter(t))
  next(b)
  return itertools.izip(a, b)

d = dict(twobytwo(t))

或者,如果你坚持要写成一行代码,正好符合“不给糖就捣蛋”的节日气氛:

d = dict((x, next(it)) for it in (iter(t),) for x in it)

我觉得这有点像恶作剧,但有些人可能会觉得这是个好主意。换句话说,我觉得这种东西有点吓人,但显然在美国,这个时候大家是 应该 这么做的;-).

基本上,这个问题可以简化为“我怎么能每次取出列表中的两个项目”,因为 dict 很乐意接受一系列的二元组(两个元素的组合),然后把它们变成字典。我在这里展示的所有解决方案都确保只占用 O(1) 的额外空间(当然,输入列表和输出字典需要的空间是 O(N))。

文档 中建议的方法(大家应该对那一页很熟悉,itertools 的食谱)是页面上的 pairwise 函数,基本上就是我在这里提到的第二个方法。我认为每个 site-packages 目录都应该包含一个 iterutils.py 文件,里面有这些食谱(真可惜这样的文件还不是 Python 标准库的一部分!-)。

撰写回答