Python:在列表中查找

我使用以下代码来检查item是否在my_list中:

if item in my_list:print("Desired item is in list")

但有时,它找不到物品。这是为什么?


if item in my_list:”是在列表中查找项目的最“pythonic”方式吗?

2879002 次浏览

至于你的第一个问题:“if item is in my_list:”完全没问题,如果item等于my_list中的一个元素,应该可以工作。项目必须完全匹配列表中的项目。例如,"abc""ABC"不匹配。特别是浮点值可能会受到不准确的影响。例如,1 - 1/3 != 2/3

至于你的第二个问题:如果在列表中“找到”东西,实际上有几种可能的方法。

检查是否有东西在里面

这是你描述的用例:检查某些东西是否在列表中。如你所知,你可以为此使用in运算符:

3 in [1, 2, 3] # => True

过滤集合

也就是说,查找序列中满足特定条件的所有元素。你可以使用列表理解或生成器表达式:

matches = [x for x in lst if fulfills_some_condition(x)]matches = (x for x in lst if x > 6)

后者将返回一个发生器,您可以将其想象为一种惰性列表,只有在您遍历它时才会构建它。顺便说一下,第一个完全等价于

matches = filter(fulfills_some_condition, lst)

在Python 2中。在这里您可以看到高阶函数在工作。在Python 3中,filter不返回列表,而是返回一个类似生成器的对象。

查找第一个出现

如果您只想要匹配条件的第一个元素(但您还不知道它是什么),可以使用for循环(也可能使用else子句,这不是很知名)。您也可以使用

next(x for x in lst if ...)

这将返回第一个匹配项,如果没有找到,则引发StopIteration。或者,您可以使用

next((x for x in lst if ...), [default value])

查找项目的位置

对于列表,还有index方法,如果你想知道在哪里某个元素在列表中,它有时会很有用:

[1,2,3].index(2) # => 1[1,2,3].index(4) # => ValueError

但是,请注意,如果您有重复项,.index总是返回最低索引:……

[1,2,3,2].index(2) # => 1

如果有重复项并且您想要所有索引,那么您可以使用enumerate()代替:

[i for i,x in enumerate([1,2,3,2]) if x==2] # => [1, 3]

如果您想找到一个元素或None,请在next中使用default,如果在列表中找不到该项目,它将不会引发StopIteration

first_or_default = next((x for x in lst if ...), None)

检查字符串列表中的项中没有额外/不需要的空白空间。这是一个原因,可以干扰解释项目无法找到。

虽然Niklas B.的答案非常全面,但当我们想在列表中找到一个项目时,有时获取其索引会很有用:

next((i for i, x in enumerate(lst) if [condition on x]), [default value])

查找第一个出现

在迭代工具中有一个配方:

def first_true(iterable, default=False, pred=None):"""Returns the first true value in the iterable.
If no true value is found, returns *default*
If *pred* is not None, returns the first itemfor which pred(item) is true.
"""# first_true([a,b,c], x) --> a or b or c or x# first_true([a,b], x, f) --> a if f(a) else b if f(b) else xreturn next(filter(pred, iterable), default)

例如,以下代码查找列表中的第一个奇数:

>>> first_true([2,3,4,5], None, lambda x: x%2==1)3

您可以复制/粘贴它或安装more-itertools

pip3 install more-itertools

这个食谱已经包括在内了。

在处理字符串列表时,您可能希望使用以下两种可能的搜索之一:

  1. 如果列表元素是平等到一个项目(“示例”在['一个','例子','两个']):

    if item in your_list: some_function_on_true()

    'ex'in['one','ex','Two']=>True

    'ex_1'in['one','ex','Two']=>False

  2. 如果list元素是喜欢一个项目('ex'在['one,'example','Two']或'example_1'在['一个','例子','两个']):

    matches = [el for el in your_list if item in el]

    matches = [el for el in your_list if el in item]

    然后只需选中len(matches)或在需要时阅读它们。

另一种选择:您可以使用if item in list:检查项目是否在列表中,但这是顺序O(n)。如果您正在处理大量项目列表,并且您需要知道的只是某些内容是否是您列表的成员,您可以首先将列表转换为集合并利用常数时间集查找

my_set = set(my_list)if item in my_set:  # much faster on average than using a list# do something

并非在所有情况下都是正确的解决方案,但在某些情况下,这可能会为您带来更好的性能。

请注意,使用set(my_list)创建集合也是O(n),所以如果你只需要这样做一次,那么这样做不会更快。如果你需要重复检查成员资格,那么在初始集合创建后的每次查找都将是O(1)。

如果在列表中找到x,则使用list.index(x)返回x的索引,如果找不到x,则返回#ValueError消息,而不是使用list.count(x)返回列表中x的出现次数(验证x确实在列表中),或者它返回0(在没有x的情况下)。关于count()的很酷的事情是它不会破坏您的代码或要求您在找不到x时抛出异常。

定义和用法

count()方法返回具有指定值的元素数。

语法

list.count(value)

例子:

fruits = ['apple', 'banana', 'cherry']
x = fruits.count("cherry")

问题示例:

item = someSortOfSelection()
if myList.count(item) >= 1 :
doMySpecialFunction(item)

如果您要检查集合中是否存在值一次,那么使用“in”运算符就可以了。但是,如果您要检查多次,那么我建议使用Bisect模块。请记住,使用Bisect模块数据必须排序。因此,您对数据进行一次排序,然后就可以使用Bisect了。在我的机器上使用Bisect模块比使用“in”运算符快12倍。

以下是使用Python 3.8及以上语法的代码示例:

import bisectfrom timeit import timeit
def bisect_search(container, value):return ((index := bisect.bisect_left(container, value)) < len(container)and container[index] == value)
data = list(range(1000))# value to searchtrue_value = 666false_value = 66666
# times to testttt = 1000
print(f"{bisect_search(data, true_value)=} {bisect_search(data, false_value)=}")
t1 = timeit(lambda: true_value in data, number=ttt)t2 = timeit(lambda: bisect_search(data, true_value), number=ttt)
print("Performance:", f"{t1=:.4f}, {t2=:.4f}, diffs {t1/t2=:.2f}")

输出:

bisect_search(data, true_value)=True bisect_search(data, false_value)=FalsePerformance: t1=0.0220, t2=0.0019, diffs t1/t2=11.71
 lstr=[1, 2, 3]lstr=map(str,lstr)r=re.compile('^(3){1}')results=list(filter(r.match,lstr))print(results)

你说在我的几次试验中,也许有空格和换行干扰。这就是为什么我给你这个解决方案。

myList=[" test","ok","ok1"]item = "test"#someSortOfSelection()if  True in list(map(lambda el : item in el ,myList)):doMySpecialFunction(item)

for_loop

def for_loop(l, target):for i in l:if i == target:return ireturn None

l = [1, 2, 3, 4, 5]print(for_loop(l, 0))print(for_loop(l, 1))# None# 1

接下来

def _next(l, target):return next((i for i in l if i == target), None)

l = [1, 2, 3, 4, 5]print(_next(l, 0))print(_next(l, 1))# None# 1

more_itertools

more_itertools.first_true(iterable, default=None, pred=None)

安装

pip install more-itertools

或直接使用

def first_true(iterable, default=None, pred=None):return next(filter(pred, iterable), default)
from more_itertools import first_true
l = [1, 2, 3, 4, 5]print(first_true(l, pred=lambda x: x == 0))print(first_true(l, pred=lambda x: x == 1))# None# 1

比较

方法时间
for_loop2.77
下一步3.64
more_itertools.first_true()3.82或10.86
import timeitimport more_itertools

def for_loop():for i in range(10000000):if i == 9999999:return ireturn None

def _next():return next((i for i in range(10000000) if i == 9999999), None)

def first_true():return more_itertools.first_true(range(10000000), pred=lambda x: x == 9999999)

def first_true_2():return more_itertools.first_true((i for i in range(10000000) if i == 9999999))

print(timeit.timeit(for_loop, number=10))print(timeit.timeit(_next, number=10))print(timeit.timeit(first_true, number=10))print(timeit.timeit(first_true_2, number=10))# 2.7730861# 3.6409407000000003# 10.869996399999998# 3.8214487000000013

in也适用于dict()s中的list()

a = [ {"a":1}, {"b":1, "c":1} ]
b = {"c":1 , "b":1} # <-- No matter the order    
if b in a:print("b is in a")

至少在Python 3.8.10中,无论顺序如何