Python:在列表中查找
Posted
技术标签:
【中文标题】Python:在列表中查找【英文标题】:Python: Find in list 【发布时间】:2012-03-21 12:19:32 【问题描述】:我遇到过这个:
item = someSortOfSelection()
if item in myList:
doMySpecialFunction(item)
但有时它不适用于我的所有项目,就好像它们在列表中未被识别(当它是字符串列表时)。
这是在列表中查找项目的最“pythonic”方式吗:if x in l:
?
【问题讨论】:
这很好,如果 item 等于myList
中的元素之一,应该可以工作。
你的意思是这是做事的好方法吗?在我的几次试验中,可能有空格和换行符相互引用......我只是想确定这是实现“在列表中查找”的好方法(通常)
令人惊讶的是,搜索如何根据条件从列表中提取子集并没有找到这个问题及其很好的答案。也许添加此评论将允许它在下次有人使用这些术语进行搜索时点击提取和/或子集。干杯。
【参考方案1】:
for_loop
def for_loop(l, target):
for i in l:
if i == target:
return i
return 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
比较
method | time/s |
---|---|
for_loop | 2.81 |
next() | 2.85 |
more_itertools.first_true() | 10.58 |
import timeit
import more_itertools
def for_loop():
for i in range(10000000):
if i == 9999999:
return i
return 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)
print(timeit.timeit(for_loop, number=10))
print(timeit.timeit(_next, number=10))
print(timeit.timeit(first_true, number=10))
# 2.8123628000000003
# 2.851581
# 10.5818328
【讨论】:
【参考方案2】:查找第一个匹配项
在 itertools 中有一个方法:
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 item
for 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 x
return next(filter(pred, iterable), default)
例如,以下代码查找列表中的第一个奇数:
>>> first_true([2,3,4,5], None, lambda x: x%2==1)
3
您可以复制/粘贴或安装more-itertools
pip3 install more-itertools
这个食谱已经包含在哪里了。
【讨论】:
谢谢。这是一个秘诀,但您必须将代码复制并粘贴到您自己的代码中,这非常愚蠢。他们为什么不把它包括在内? Ruby 有Enumerable#find
,这是一个经典的例子,说明它的库的用户友好性比 Python 领先几光年。
@AdamSpiers pip install more-itertools
谢谢,我猜你的意思是more-itertools.first_true()
。很高兴知道这一点,但是没有一种优雅的方法可以用语言或标准库本地实现这一点,这仍然是荒谬的。需要默认值的 next
hack 很麻烦。
@AdamSpiers Afaik 他们不希望 python 变成 lisp 或 haskell。拥有完整范围的功能工具将使用 python 编写的程序与用函数式语言编写的程序一样难以阅读。然而,我个人也很怀念语言或标准库中的那些功能。
@AdamSpiers 我不能 100% 确定他们没有其他动机,这只是我知道的唯一理由。我发现 ruby 的语法比 python 的可读性差。你知道,如果你包含来自函数式语言的所有关键字,那么下一个问题将是“为什么完全相同的构造在 python 中的运行速度比在 haskell 中慢 x 倍”。不包括它们只是一个提示,如果你喜欢它们,也许 python 是错误的语言来编写它们;) 可读性首先取决于作者。 Python 只是努力让那些喜欢编写不可读代码的人的生活变得更加困难:)【参考方案3】:
你说在我的几次试验中,可能有空格和换行干扰。这就是我给你这个解决方案的原因。
myList=[" test","ok","ok1"]
item = "test"#someSortOfSelection()
if True in list(map(lambda el : item in el ,myList)):
doMySpecialFunction(item)
【讨论】:
【参考方案4】: lstr=[1, 2, 3]
lstr=map(str,lstr)
r=re.compile('^(3)1')
results=list(filter(r.match,lstr))
print(results)
【讨论】:
【参考方案5】:如果您要检查收藏品中是否存在价值,那么使用“in”运算符就可以了。但是,如果您要检查不止一次,那么我建议使用 bisect 模块。请记住,必须对使用 bisect 模块的数据进行排序。因此,您对数据进行一次排序,然后就可以使用 bisect。在我的机器上使用 bisect 模块比使用 'in' 运算符快大约 12 倍。
以下是使用 Python 3.8 及以上语法的代码示例:
import bisect
from 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 search
true_value = 666
false_value = 66666
# times to test
ttt = 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)=False
Performance: t1=0.0220, t2=0.0019, diffs t1/t2=11.71
【讨论】:
【参考方案6】:定义和用法
count()
方法返回具有指定值的元素个数。
语法
list.count(value)
示例:
fruits = ['apple', 'banana', 'cherry']
x = fruits.count("cherry")
问题示例:
item = someSortOfSelection()
if myList.count(item) >= 1 :
doMySpecialFunction(item)
【讨论】:
这在很长的列表中是否有效?说一百万的名单?【参考方案7】:您可以使用list.count(x)
,而不是使用list.index(x)
,如果它在列表中找到则返回x 的索引或返回#ValueError
消息,它返回列表中x 的出现次数(验证 x 确实在列表中),否则返回 0(在没有 x 的情况下)。 count()
很酷的一点是它不会破坏您的代码或要求您在未找到 x 时抛出异常
【讨论】:
不好的是它计数元素。找到元素时它不会停止。所以在大列表上性能很差【参考方案8】:另一种选择:您可以使用if item in list:
检查项目是否在列表中,但这是 O(n) 的顺序。如果您正在处理大量项目并且您只需要知道某项是否属于您的列表的成员,您可以先将列表转换为集合并利用constant time set lookup:
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)。
【讨论】:
【参考方案9】:在处理字符串列表时,您可能希望使用两种可能的搜索之一:
如果列表元素 equal 与某个项目('example' 在 ['一个','例子','两个']):
if item in your_list: some_function_on_true()
'ex' in ['one','ex','two'] => 真
'ex_1' in ['one','ex','two'] => 假
如果列表元素喜欢一个项目('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)
或在需要时阅读它们。
【讨论】:
【参考方案10】:至于您的第一个问题:如果item
等于myList
中的一个元素,那么该代码就可以正常工作。也许您试图找到一个与其中一项不完全匹配的字符串,或者您正在使用一个不准确的浮点值。
至于你的第二个问题:如果在列表中“找到”东西,实际上有几种可能的方法。
检查里面是否有东西
这是您描述的用例:检查某物是否在列表中。如您所知,您可以使用 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)
后者将返回一个 generator,您可以将其想象为一种惰性列表,只有在您遍历它时才会构建它。顺便说一句,第一个完全等价于
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]
【讨论】:
Stephane:让我重新表述一下:if x in list
是不是人们抱怨不是内置函数的东西。他们抱怨这样一个事实,即没有明确的方法可以在列表中找到与特定条件匹配的事物的第一次出现。但正如我的回答中所述,next()
可以(ab)用于此目的。
@Stephane:第二个不会生成元组,而是生成器(基本上是尚未构建的列表)。如果您只想使用一次结果,通常最好使用生成器。但是,如果您想在之后多次使用创建的集合,建议首先创建一个显式列表。看看我的更新,现在结构更好了:)
你的“寻找第一次出现”的例子是黄金。感觉比 [list comprehension...][0]
方法更 Pythonic
我对 python 的“功能”功能越来越失望。在 haskell 中,Data.List 模块中的 find 函数正是这样做的。但是在 python 中它不是,而且它很小,所以你必须一遍又一遍地重新实现相同的逻辑。真是浪费……
如果有一个名为key
的index()
的kwarg 就像key
接受的max()
一样工作,那就太好了;例如:index(list, key=is_prime)
.【参考方案11】:
虽然 Niklas B. 的回答非常全面,但当我们想在列表中查找项目时,获取其索引有时会很有用:
next((i for i, x in enumerate(lst) if [condition on x]), [default value])
【讨论】:
【参考方案12】:检查字符串列表的项目中没有额外的/不需要的空格。 这可能会干扰解释无法找到项目的原因。
【讨论】:
【参考方案13】:如果你想找到一个元素或None
在next
中使用默认值,如果在列表中找不到该项目,它不会引发StopIteration
:
first_or_default = next((x for x in lst if ...), None)
【讨论】:
next
将迭代器作为第一个参数,列表/元组不是迭代器。所以应该是first_or_default = next(iter([x for x in lst if ...]), None)
见docs.python.org/3/library/functions.html#next
@Devy:没错,但(x for x in lst if ...)
是列表lst
的生成器(是 一个迭代器)。如果你做next(iter([x for x in lst if ...]), None)
,你必须构造列表[x for x in lst if ...]
,这将是一个更昂贵的操作。
这里有一个抽象来定义一个查找函数。只需将 if
的布尔表达式封装在 lambda 中,您通常可以编写 find(fn,list)
而不是混淆生成器代码。以上是关于Python:在列表中查找的主要内容,如果未能解决你的问题,请参考以下文章