用Pythonic方式将列表元素与其索引关联起来

9
我可以把一组数值放到字典里,让每个数值都对应它的索引。以下是其中一种实现方法:
>>> 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倍,并且函数式map(reversed)要慢得多。 $ 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? - Hamish Grubijan
6个回答

14

您可以使用列表推导式(或生成器,取决于您的Python版本)来执行第二个示例的简单原地交换。


使用列表推导式:

d = dict([(y,x) for x,y in enumerate(t)])

使用生成器表达式(Python 2.4及以上版本):
d = dict((y,x) for x,y in enumerate(t))

3
不需要在那里使用[]。使用生成器表达式就可以在dict中正常工作(可以避免创建中间列表)。 - John La Rooy
1
是的,这就是为什么我写了“取决于你的Python版本”。生成器已经存在很长时间了(自2.4版本以来),所以我会包含两个版本。 - kibibu
2
@J.F.Sebastian,2004年之前开发的部署系统中?Python已经存在相当长的时间了。很容易想象有必要工作在一些Python 2.0应用上,我的意思是,有些人仍然需要在VB6上工作。 - kibibu

14
在Python2.7+中,您可以这样编写代码:
>>> t = (5,6,7)
>>> d = {x:i for i,x in enumerate(t)}
>>> print d
{5: 0, 6: 1, 7: 2}

4
>>> dict((x,i) for i,x in enumerate(t))
{5: 0, 6: 1, 7: 2}
>>>

2

正如其他人已经写过的,在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}

2
你的所有元素都是唯一的吗(即,你的列表永远不会是5、6、7、7)?如果所有元素都是唯一的,那么字典解决方案只能起作用。
通过存储索引,实际上是在复制信息,因为你可以简单地查询列表中项目的当前索引。复制信息通常不是最好的想法,因为它允许一个数据集与另一个数据集不同步。
如果正在修改列表,也没有任何防止你意外地将相同的索引分配给多个项目的方法。
当你可以从列表中获取索引时,为什么要尝试存储索引值呢?

所有列表元素都是唯一的。我将索引存储在不同的数据结构中以进行快速查找。 - AnalyticsBuilder
如果所有元素都是唯一的,那么这听起来像是一个无用的间接层次。使用 in 进行成员测试并使用 index() 进行索引。我猜想你认为哈希映射支持的字典将比 index() 更快地查找。在 Python 中,过早地进行优化真的很糟糕,因为你对“更快”的直觉通常是错误的,直到实际计时。让它工作,然后找出你的慢点在哪里,增加复杂性是不值得的。 - msw
@Dragan,你是在修改你的列表还是保持不变? - Hamish Grubijan
@msw 你的担忧是合理的,总体而言我同意你的看法,在这种情况下我认为值得这样做。python -mtimeit -s"t = range(int(1e2))" "truthVal = (55 in t)"

每次循环3.11微秒

python -mtimeit -s"t = range(int(1e2)); d = dict(zip(t, range(len(t))))" "truthVal = (55 in d)"

每次循环0.138微秒

python -mtimeit -s"t = range(int(1e2))" "idxVal = t.index(55)"

每次循环3.46微秒

python -mtimeit -s"t = range(int(1e2)); d = dict(zip(t, range(len(t))))" "indexVal = d[55]"

每次循环0.136微秒

- AnalyticsBuilder
@dragan:断言确认,即使我通过将“5”作为目标来大幅偏向您的测试,字典仍然比列表快3到7倍。我认为缓存命中/未命中可能会影响结果,但在我的系统上似乎不是这样,即使使用1e7大小的列表也会强制其转换到交换空间。(Python 2.6.5 Linux 2.6) - msw

0

我最喜欢的是 dict(zip(t, range(len(t))))。


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