如何在Python中搜索元组列表

hdx*_*hdx 88 python search tuples list

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

[(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]
Run Code Online (Sandbox Code Playgroud)

我希望这个列表的元组值等于某个元组.

所以,如果我这样做search(53)将返回索引值2

是否有捷径可寻?

Ign*_*ams 86

[i for i, v in enumerate(L) if v[0] == 53]
Run Code Online (Sandbox Code Playgroud)

  • 你能解释一下吗? (65认同)
  • 用文字解释:对于每个i,v在L的枚举列表中(使得我在枚举列表中的元素位置和v原始元组)检查元组的第一个元素是否为53,如果是,则追加代码的结果在'for'到新创建的列表之前,这里:i.它也可以是my_function(i,v)或另一个列表理解.由于您的元组列表只有一个元组,其中53为第一个值,因此您将获得一个包含一个元素的列表. (16认同)
  • 我只是添加[i for i,v in enumerate(L)if v [0] == 53] .pop()得到int值. (4认同)

Gre*_*ill 48

您可以使用列表理解:

>>> 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
Run Code Online (Sandbox Code Playgroud)


Jon*_*ell 43

TL;博士

一个生成器表达式可能是最高效和简单的解决问题的方法:

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

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

说明

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

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

Python提供了一个简单的构造,在这里是理想的.它被称为生成器表达式.这是一个例子:

# 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)
Run Code Online (Sandbox Code Playgroud)

我们可以期望这个方法在我们简单的例子中与列表推导基本相同,但是如果我们使用更大的数据集呢?这就是使用发电机方法的优势发挥作用的地方.我们将使用现有列表作为迭代,而不是构建新列表,并使用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}
Run Code Online (Sandbox Code Playgroud)

"最好的案例"

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}
Run Code Online (Sandbox Code Playgroud)

生成器表达式

这是我对发电机的假设:我们会看到发电机在最好的情况下表现会更好,但在最坏的情况下也是如此.这种性能增益主要是由于生成器被懒惰地评估,这意味着它只计算产生值所需的内容.

最糟糕的情况

# 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}
Run Code Online (Sandbox Code Playgroud)

最好的情况

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}
Run Code Online (Sandbox Code Playgroud)

什么?!最好的情况打破了列表理解,但我并不期望我们最坏的情况在这种程度上超越列表理解.那个怎么样?坦率地说,我只能在没有进一步研究的情况下推测.

考虑到所有这些,我没有在这里进行任何强大的分析,只是一些非常基本的测试.这应该足以理解生成器表达式对于这种类型的列表搜索更具性能.

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

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

  • 我认为这应该更多 (6认同)
  • 有趣的是,我测试并发现它非常快 (2认同)
  • 这应该是公认的答案。**生成器表达式在运行时不会具体化整个输出序列,但它们的计算结果是一个迭代器,该迭代器一次可从该表达式中产生一项。** (2认同)
  • 这很棒,在我的例子中比列表理解快得多,谢谢! (2认同)

And*_*ffe 26

你的元组基本上是键值对 - 一个python dict--so:

l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]
val = dict(l)[53]
Run Code Online (Sandbox Code Playgroud)

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

d = dict((n,i) for (i,n) in enumerate(e[0] for e in l))
idx = d[53]
Run Code Online (Sandbox Code Playgroud)

  • 如果我们忽略了OP的实际要求,那么我认为您的初始答案是“如何在Python中搜索元组列表”的最佳答案。 (2认同)

Dav*_*d Z 12

嗯......好吧,想到的简单方法就是将它转换成字典

d = dict(thelist)
Run Code Online (Sandbox Code Playgroud)

和访问d[53].

编辑:哎呀,第一次误读了你的问题.听起来你真的想要获得存储给定数字的索引.在那种情况下,试试吧

dict((t[0], i) for i, t in enumerate(thelist))
Run Code Online (Sandbox Code Playgroud)

而不是一个普通的旧dict转换.那d[53]就是2.


Gra*_*ntJ 6

假设列表可能很长并且数字可能重复,请考虑使用Python sortedcontainers模块中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]
Run Code Online (Sandbox Code Playgroud)

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

如果存在具有不同字符串的重复数字,则需要再执行一个步骤:

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

results = sl[index:end]
Run Code Online (Sandbox Code Playgroud)

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