如何在 Python 中搜索元组列表

Posted

技术标签:

【中文标题】如何在 Python 中搜索元组列表【英文标题】:How to search a list of tuples in Python 【发布时间】:2011-02-24 10:23:25 【问题描述】:

所以我有一个这样的元组列表:

[(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]

我想要这个列表用于一个数值等于某个值的元组。

所以如果我执行search(53),它将返回2 的索引值

有没有简单的方法可以做到这一点?

【问题讨论】:

【参考方案1】:
[i for i, v in enumerate(L) if v[0] == 53]

【讨论】:

用文字解释:对于L的枚举列表中的每个i,v(这使得i元素在枚举列表中的位置和v成为原始元组)检查元组的第一个元素是否为53,如果因此,将“for”之前的代码结果附加到新创建的列表中,此处:i。它也可以是 my_function(i, v) 或另一个列表理解。由于您的元组列表只有一个以 53 作为第一个值的元组,因此您将获得一个包含一个元素的列表。 如果 v[0] == 53].pop() 具有 int 值,我只需将 [i for i, v in enumerate(L) 添加。【参考方案2】:

tl;博士

generator expression 可能是解决您的问题的最有效和最简单的解决方案:

l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]

result = next((i for i, v in enumerate(l) if v[0] == 53), None)
# 2

说明

有几个答案可以通过列表推导为这个问题提供一个简单的解决方案。 虽然这些答案是完全正确的,但它们并不是最优的。 根据您的使用案例,进行一些简单的修改可能会有很大的好处。

我看到在这个用例中使用列表推导的主要问题是 整个列表 将被处理,尽管您只想找到 1 个元素

Python 提供了一个简单的结构,在这里非常理想。它被称为generator expression。这是一个例子:

# Our input list, same as before
l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]

# Call next on our generator expression.
next((i for i, v in enumerate(l) if v[0] == 53), None)

我们可以期待这个方法的执行与我们简单示例中的列表推导基本相同,但是如果我们使用更大的数据集怎么办? 这就是使用生成器方法的优势发挥作用的地方。 我们不会构建一个新列表,而是使用您现有的列表作为我们的可迭代对象,并使用next() 从我们的生成器中获取第一项。

让我们看看这些方法在一些更大的数据集上的表现有何不同。 这些是由 10000000 + 1 个元素组成的大型列表,我们的目标位于开头(最佳)或结尾(最差)。 我们可以使用以下列表推导来验证这两个列表的性能是否相同:

列表推导

“最坏情况”

worst_case = ([(False, 'F')] * 10000000) + [(True, 'T')]
print [i for i, v in enumerate(worst_case) if v[0] is True]

# [10000000]
#          2 function calls in 3.885 seconds
#
#    Ordered by: standard name
#
#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
#         1    3.885    3.885    3.885    3.885 so_lc.py:1(<module>)
#         1    0.000    0.000    0.000    0.000 method 'disable' of '_lsprof.Profiler' objects

“最佳情况”

best_case = [(True, 'T')] + ([(False, 'F')] * 10000000)
print [i for i, v in enumerate(best_case) if v[0] is True]

# [0]
#          2 function calls in 3.864 seconds
#
#    Ordered by: standard name
#
#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
#         1    3.864    3.864    3.864    3.864 so_lc.py:1(<module>)
#         1    0.000    0.000    0.000    0.000 method 'disable' of '_lsprof.Profiler' objects

生成器表达式

这是我对生成器的假设:我们将看到生成器在最好的情况下会表现得更好,但在最坏的情况下也是如此。 这种性能提升主要是由于生成器的评估是惰性的,这意味着它只会计算产生值所需的内容。

最坏情况

# 10000000
#          5 function calls in 1.733 seconds
#
#    Ordered by: standard name
#
#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
#         2    1.455    0.727    1.455    0.727 so_lc.py:10(<genexpr>)
#         1    0.278    0.278    1.733    1.733 so_lc.py:9(<module>)
#         1    0.000    0.000    0.000    0.000 method 'disable' of '_lsprof.Profiler' objects
#         1    0.000    0.000    1.455    1.455 next

最佳情况

best_case  = [(True, 'T')] + ([(False, 'F')] * 10000000)
print next((i for i, v in enumerate(best_case) if v[0] == True), None)

# 0
#          5 function calls in 0.316 seconds
#
#    Ordered by: standard name
#
#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
#         1    0.316    0.316    0.316    0.316 so_lc.py:6(<module>)
#         2    0.000    0.000    0.000    0.000 so_lc.py:7(<genexpr>)
#         1    0.000    0.000    0.000    0.000 method 'disable' of '_lsprof.Profiler' objects
#         1    0.000    0.000    0.000    0.000 next

什么?!最好的情况吹走列表推导,但我没想到我们最坏的情况会在这种程度上胜过列表推导。 那个怎么样?坦率地说,我只能推测没有进一步的研究。

对所有这些都持保留态度,我在这里没有运行任何强大的分析,只是一些非常基本的测试。这应该足以理解生成器表达式对于这种类型的列表搜索的性能更高。

请注意,这都是基本的内置 python。我们不需要导入任何东西或使用任何库。

我第一次看到这种搜索技术是在 Peter Norvig 的 Udacity cs212 课程中。

【讨论】:

有趣,我测试过,发现它真的很快 这应该是公认的答案。 生成器表达式在运行时不会具体化整个输出序列,而是会计算出一个迭代器,从表达式中一次生成一个项目。 这很棒,比我的列表理解要快得多,谢谢!【参考方案3】:

您可以使用list comprehension:

>>> a = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]
>>> [x[0] for x in a]
[1, 22, 53, 44]
>>> [x[0] for x in a].index(53)
2

【讨论】:

【参考方案4】:

您的元组基本上是键值对--a python dict--so:

l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]
val = dict(l)[53]

编辑——啊哈,你说你想要(53,“xuxa”)的索引值。如果这真的是你想要的,你将不得不遍历原始列表,或者可能制作一个更复杂的字典:

d = dict((n,i) for (i,n) in enumerate(e[0] for e in l))
idx = d[53]

【讨论】:

如果我们忽略 OP 实际要求的内容,我认为您的初始答案是“如何在 Python 中搜索元组列表”的最佳答案 您的第一个答案对我的目的很有用。也许更好地使用 .get() ,以防项目不在字典中。 l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")] val = dict(l).get(53)【参考方案5】:

嗯...好吧,想到的简单方法是将其转换为字典

d = dict(thelist)

并访问d[53]

编辑:糟糕,第一次误读了您的问题。听起来您实际上想要获取存储给定数字的索引。在这种情况下,请尝试

dict((t[0], i) for i, t in enumerate(thelist))

而不是普通的旧 dict 转换。那么d[53] 将是 2。

【讨论】:

【参考方案6】:

假设列表可能很长并且数字可能重复,请考虑使用Python sortedcontainers module 中的SortedList 类型。 SortedList 类型将自动按数字顺序维护元组并允许快速搜索。

例如:

from sortedcontainers import SortedList
sl = SortedList([(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")])

# Get the index of 53:

index = sl.bisect((53,))

# With the index, get the tuple:

tup = sl[index]

通过进行二分搜索,这将比列表理解建议快得多。字典建议会更快,但如果可能存在具有不同字符串的重复数字,则该字典建议将不起作用。

如果存在不同字符串的重复数字,则需要再采取一步:

end = sl.bisect((53 + 1,))

results = sl[index:end]

通过对 54 进行二等分,我们将找到切片的结束索引。与接受的答案相比,这在长列表上会明显更快。

【讨论】:

【参考方案7】:

只是另一种方式。

zip(*a)[0].index(53)

【讨论】:

【参考方案8】:

[k for k,v in l if v =='delicia']

这里 l 是元组列表-[(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]

我们没有将其转换为字典,而是使用 llist 理解。

*Key* in Key,Value in list, where value = **delicia**

【讨论】:

是的,当然。谢谢@cosmoonot。 这里 l 是元组列表-[(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")] 和我们没有将其转换为字典,而是使用列表理解。 ` Key in Key,Value in list, where value = delicia `

以上是关于如何在 Python 中搜索元组列表的主要内容,如果未能解决你的问题,请参考以下文章

如何使用标头映射在python中编写元组列表

如何在 Python 中绘制元组列表?

在python中,我应该如何在元组列表上实现最小堆?

如何在python中切片元组列表?

Python面试必考重点之列表,元组和字典第一关——如何定义集合/集合与列表有什么区别/如何去掉列表(元组)中重复的元素

如何在 Python 中对元组列表列表进行平面映射? [复制]