如何在Python中search元组列表
所以我有这样的元组列表:
[(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]
我想要这个列表中的数字值相等的东西。
所以,如果我search(53)
它将返回索引值2
是否有捷径可寻?
[i for i, v in enumerate(L) if v[0] == 53]
你可以使用列表理解 :
>>> 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
TL;博士
生成器expression式可能是您的问题的最高性能和简单的解决scheme:
l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")] result = next((i for i, v in enumerate(l) if v[0] == 53), None) # 2
说明
有几个答案,提供一个简单的解决这个问题与列表parsing。 虽然这些答案是完全正确的,但并不是最优的。 根据您的使用情况,进行一些简单的修改可能会有很大的好处。
我在使用列表理解这个用例时看到的主要问题是整个列表将被处理,尽pipe你只想find1个元素 。
Python提供了一个简单的构造,在这里是理想的。 它被称为生成器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)
我们可以期望这个方法在我们的例子中与列表parsing基本相同,但是如果我们正在处理更大的数据集呢? 这就是使用发生器方法的好处。 我们将使用现有的列表作为我们的迭代器,而不是使用next()
从我们的生成器中获取第一个项目。
让我们看看这些方法在一些更大的数据集上的performance如何。 这些是由10000000 + 1个元素组成的大列表,其中我们的目标在开始(最好)或结束(最差)。 我们可以使用以下列表理解来validation这两个列表是否能够平等地执行:
列表parsing
“最坏的情况下”
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}
生成器expression式
以下是我对发电机的假设:我们将看到发电机在最好的情况下将显着地performance更好,但在最坏的情况下也是如此。 这种性能增益主要是由于发生器是懒惰评估的,这意味着它只会计算产生一个值所需的值。
最坏的情况下
# 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}
什么?! 最好的情况是吹掉了列表理解,但是我并没有想到我们最糟糕的情况是在这样的程度上超越了列表理解。 那个怎么样? 坦率地说,我只能推测没有进一步的研究。 也许我会问Stack Overflow。
把所有这一切用盐,我没有在这里运行任何可靠的分析,只是一些非常基本的testing。 我想包括一些内存分析,但这些技术逃脱我。 然而,我们在这里看到的是足以看到,对于这种types的列表search,生成器expression式更有效。
请注意,这是所有基本的,内置的Python。 我们不需要导入任何东西或使用特殊的库。
为了在信用到期时给予信贷,我首先在Peter Norvig的免费课程Udacity cs212中看到了这种技巧。
你的元组基本上是键值对 – 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]
嗯…嗯,想到的简单方法是将其转换为字典
d = dict(thelist)
并访问d[53]
。
编辑 :哎呀,第一次误读你的问题。 这听起来像你实际上想要得到一个给定的数字存储在索引。 在这种情况下,试试
dict((t[0], i) for i, t in enumerate(thelist))
而不是一个普通的老dict
转换。 那么d[53]
就是2。
假设列表可能很长并且数字可能会重复,请考虑使用Python sortedclustainers模块中的SortedListtypes。 SortedListtypes将按数字顺序自动维护元组,并允许快速search。
例如:
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]
这会比通过进行二分查找的列表理解build议快得多。 字典build议将会更快,但如果可能有重复的数字与不同的string将无法正常工作。
如果有不同的string重复的数字,那么你需要多一个步骤:
end = sl.bisect((53 + 1,)) results = sl[index:end]
通过平分54,我们将find我们切片的结束指数。 与被接受的答案相比,这将在长列表上显着更快。
只是另一种方式。
zip(*a)[0].index(53)
[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**