将有序列表转换为字典的Pythonic方法
我似乎找不到一个优雅的方法,从 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}
这里使用了dict
、zip
和iter
这几个函数。它比Lukáš的回答更好的地方在于,它适用于任何可迭代的对象。下面是它的工作原理:
iter(t)
会创建一个针对列表t
的迭代器。[iter(t)] * 2
会创建一个包含两个元素的列表,这两个元素都指向同一个迭代器。zip
是一个函数,它可以接收两个可迭代对象,并将它们的元素配对:第一个元素配对在一起,第二个元素配对在一起,依此类推,直到其中一个可迭代对象用完。zip(*([iter(t)] * 2))
会将同一个迭代器作为两个参数传给zip
。这样,zip
就会将t
的第一个和第二个元素配对,然后是第三个和第四个,接着是第五个和第六个,依此类推。dict
会接收一个包含(key, value)
对的可迭代对象,并根据这些对创建一个字典。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 标准库的一部分!-)。