将列表元素与其索引相关联的pythonic方法
Posted
技术标签:
【中文标题】将列表元素与其索引相关联的pythonic方法【英文标题】:pythonic way to associate list elements with their indices 【发布时间】:2011-02-19 08:58:55 【问题描述】:我有一个值列表,我想将它们放在一个字典中,该字典会将每个值映射到它的索引。
我可以这样做:
>>> t = (5,6,7)
>>> d = dict(zip(t, range(len(t))))
>>> d
5: 0, 6: 1, 7: 2
这还不错,但我正在寻找更优雅的东西。
我遇到了以下情况,但这与我需要的相反:
>>> d = dict(enumerate(t))
>>> d
0: 5, 1: 6, 2: 7
请分享您的解决方案, 谢谢
编辑:Python 2.6.4
对于包含 1000 个元素的列表,dict(zip) 版本是最快的,生成器和列表理解版本几乎相同,它们慢了约 1.5 倍,而功能映射(反转)则慢得多。
$ python -mtimeit -s"t = range(int(1e3))" "d = dict(zip(t, range(len(t))))" 1000 个循环,最好的 3 个:每个循环 277 微秒
$ python -mtimeit -s"t = range(int(1e3))" "d = dict([(y,x) for x,y in enumerate(t)])" 1000 个循环,最好的 3 个:每个循环 426 微秒
$ python -mtimeit -s"t = range(int(1e3))" "d = dict((y,x) for x,y in enumerate(t))" 1000 个循环,最好的 3 个:每个循环 437 微秒
$ python -mtimeit -s"t = range(int(1e3))" "d = dict(map(reversed, enumerate(t)))" 100 个循环,3 个循环中的最佳:每个循环 3.66 毫秒
我尝试对更长和更短的列表(1e2、1e4、1e5)运行相同的测试,每个循环的时间与列表的长度成线性关系。
有人可以给 py 2.7+ 版本计时吗?
【问题讨论】:
我很好奇 - 哪个实现更快?顺便问一下,Chewy,你用的是哪个版本的 Python? 【参考方案1】:您可以使用列表推导式(或生成器,具体取决于您的 python 版本)为您的第二个示例执行简单的就地交换。
使用列表推导:
d = dict([(y,x) for x,y in enumerate(t)])
使用生成器表达式(Python 2.4 及更高版本):
d = dict((y,x) for x,y in enumerate(t))
【讨论】:
那里不需要[]
。 dict
可以很好地与生成器表达式配合使用(保存生成中间列表)
是的,这就是我写“取决于你的 python 版本”的原因。生成器已经存在了很长时间(从 2.4 开始),所以我将两者都包括在内
@J.F. Sebastian 在 2004 年之前开发的部署系统中? Python 已经存在了很长一段时间了。不难想象必须在一些 Python 2.0 应用程序上工作,我的意思是有些人仍然必须在 VB6 中工作。【参考方案2】:
在Python2.7+可以这样写
>>> t = (5,6,7)
>>> d = x:i for i,x in enumerate(t)
>>> print d
5: 0, 6: 1, 7: 2
【讨论】:
【参考方案3】:>>> dict((x,i) for i,x in enumerate(t))
5: 0, 6: 1, 7: 2
>>>
【讨论】:
【参考方案4】:您的所有元素都是独一无二的吗(即您的列表永远不会是 5、6、7、7)? dict 解决方案仅在您的所有元素都是唯一的情况下才有效。
通过存储索引,您实际上是在复制信息,因为您可以简单地查询列表中项目的当前索引。复制信息通常不是最好的主意,因为它可能会导致一组数据与另一组数据不同步。
如果正在修改列表,也没有什么可以阻止您不小心将同一索引分配给多个项目。
当您可以简单地从列表中获取索引时,您为什么要尝试存储索引值?
【讨论】:
所有列表元素都是唯一的。我将索引存储在不同的数据结构中以便快速查找。 如果所有元素都是唯一的,这听起来像是一个无用的间接级别,使用in
测试成员资格并使用index()
进行索引。我猜你认为哈希映射支持的字典会给你比index()
更快的查找速度。在 Python 中,过早优化确实是邪恶的,因为您对“更快”的直觉通常是错误的,直到实际计时。让它工作,然后找出你慢的地方,增加复杂性是不值得的。
@Dragan,您是在修改列表还是保持静态?
@msw 有效的关注,总的来说我同意你,在这种情况下我认为这是值得的 python -mtimeit -s"t = range(int(1e2))" "truthVal = (55 in t)" # 3.11 usec per loop python -mtimeit -s"t = range(int(1e2)); d = dict(zip(t, range(len(t))))" "truthVal = (55 in d )" #0.138 usec per loop python -mtimeit -s"t = range(int(1e2))" "idxVal = t.index(55)" #3.46 usec per loop python -mtimeit -s"t = range(int( 1e2)); d = dict(zip(t, range(len(t))))" "indexVal = d[55]" #0.136 usec per loop
@dragan:断言得到证实,即使我使用“5”作为目标,使您的测试严重偏向字典,但仍然以 3 到 7 倍的速度将门从列表中炸开。我认为缓存命中/未命中可能令人困惑,即使列表大小为 1e7 迫使它进行交换,它似乎也不适合我的系统。 (Python 2.6.5 Linux 2.6)【参考方案5】:
正如大家已经写过的,在 Python 2.6 中,我认为以下是最 Python 的:
>>> dict((x, i) for i, x in enumerate(t))
5: 0, 6: 1, 7: 2
不过,在功能***的时刻,我会写:
>>> dict(map(reversed, enumerate(t)))
5: 0, 6: 1, 7: 2
【讨论】:
【参考方案6】:我最喜欢 dict(zip(t, range(len(t))))。
【讨论】:
以上是关于将列表元素与其索引相关联的pythonic方法的主要内容,如果未能解决你的问题,请参考以下文章