如何在Python中搜索元组列表

103 投票
8 回答
142599 浏览
提问于 2025-04-15 23:13

我有一个包含多个元组的列表,像这样:

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

我想从这个列表中找到一个元组,它的数字值等于某个特定的值。

比如说,如果我输入 search(53),它应该返回索引值 2

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

8 个回答

50

你可以使用一种叫做 列表推导式 的方法:

>>> 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
69

总结

一种叫做生成器表达式的方法,可能是解决你问题的最快和最简单的办法:

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

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

解释

有很多答案用列表推导提供了简单的解决方案。虽然这些答案完全正确,但并不是最优的。根据你的使用场景,做一些简单的修改可能会带来很大的好处。

我认为使用列表推导的主要问题是,你会处理整个列表,而你其实只想找一个元素

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)

在我们这个简单的例子中,使用这种方法的表现基本上和列表推导差不多,但如果我们处理的是更大的数据集呢?这时候使用生成器的方法就显得有优势了。我们不需要构建一个新的列表,而是直接使用你现有的列表作为可迭代对象,然后用next()来获取生成器的第一个项目。

让我们看看这些方法在一些更大的数据集上的表现差异。这些都是包含超过10000000个元素的大列表,目标元素在开头(最好情况)或结尾(最坏情况)。我们可以通过以下的列表推导来验证这两个列表的表现是一样的:

列表推导

最坏情况

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内置的基本功能。我们不需要导入任何东西或使用任何库。

我第一次看到这种搜索技术是在Udacity cs212课程中,讲师是彼得·诺维格。

109
[i for i, v in enumerate(L) if v[0] == 53]

这段代码的意思是……

首先,它做了一个什么操作,然后又进行了另一个操作。接下来,它会检查一些条件,如果满足这些条件,就会执行特定的代码块。最后,它可能会返回一个结果或者继续进行其他的处理。

总之,这段代码的主要目的是……

希望这个解释能帮助你更好地理解这段代码的功能!

撰写回答