Python 的 list.index() 函数,当没有找到时不会抛出异常

Posted

技术标签:

【中文标题】Python 的 list.index() 函数,当没有找到时不会抛出异常【英文标题】:list.index() function for Python that doesn't throw exception when nothing found 【发布时间】:2012-01-02 02:19:15 【问题描述】:

如果项目不存在,Python 的 list.index(x) 会引发异常。有没有更好的不需要处理异常的方法?

【问题讨论】:

视情况而定。你关心它在哪里吗? 最好的方法取决于你在什么都找不到的情况下该怎么做。即使我们有返回 -1 的 list.find,您仍然需要测试以查看是否为 i == -1 并采取一些措施。 Raymond- 似乎应该由我来决定我的代码是否可以处理 None 索引,而不是强制异常。但是,我仍在学习如何成为 Pythonic... Why list doesn't have safe "get" method like dictionary?的可能重复 @yarin,正如我在回答中澄清的那样,如果您不需要索引,in 关键字就是您所需要的。但如果你这样做了,我认为 Raymond 有一个很好的观点,例外是解决这个问题的最佳方法。 【参考方案1】:

如果你不关心匹配元素在哪里,那么使用:

found = x in somelist

如果您在乎,请使用LBYL 样式和conditional expression:

i = somelist.index(x) if x in somelist else None

【讨论】:

感谢 Raymond,发现这是最简洁的答案(同样,将默认值从 -1 更改为 None,因为 -1 是有效的列表索引) 谢谢雷蒙德,LBYL 对我来说是新的参考。 但是这种方法不是比只运行index() 慢很多吗?毕竟你必须看两次:一次是为了存在,一次是为了索引。这就是为什么 C++ 容器没有exists(),只有find() 正如@frans 所说,这需要两次查找,但这是另一种可以一次性完成工作的方法:i = next((i for i, t in enumerate(somelist) if x == t), None) @AXO 虽然是一次性的,但是对于内置类型来说它会更慢,因为查找是由 Python 而不是 C 代码执行的。【参考方案2】:

TL;DR:例外是您的朋友,也是解决上述问题的最佳方法。 请求宽恕比请求许可更容易 (EAFP)

OP 在评论中澄清说,对于他们的用例,知道索引是什么实际上并不重要。正如公认的答案所指出的,如果您不在乎,使用x in somelist 是最好的答案。

但我会假设,正如最初的问题所暗示的那样,您确实关心索引是什么。在这种情况下,我会注意到所有其他解决方案都需要扫描列表两次,这会带来很大的性能损失。

此外,正如可敬的 Raymond Hettinger 在评论中所写的那样

即使我们有返回 -1 的 list.find,您仍然需要测试 i == -1 并采取一些措施。

因此,我将推翻原始问题中应避免异常的假设。我建议例外是你的朋友。它们没什么好怕的,它们也不是低效的,事实上你需要熟悉它们才能写出好的代码。

所以我认为最好的答案是简单地使用 try-except 方法:

try:
    i = somelist.index(x) 
except ValueError:
    # deal with it

处理它”只是意味着做你需要做的事情:将 i 设置为哨兵值,引发你自己的异常,遵循不同的代码分支,等等。

这是 Python 原理 Easier to ask for forgiveness than permission (EAFP) 与 Look before you leap (LBYL) 的 if-then-else 风格形成对比的一个示例

【讨论】:

如此真实!我以为我遇到了这个问题,起初认为其他解决方案之一很好。然后我再次查看我的代码,并意识到使用 try...except 会更快更自然,因为当然我仍然必须处理它。 ... 我笑出声来。有时洞察力不会持久。我只是在上面发表了评论,没有注意到我在 2 年前就已经写了答案。只有当我试图投票给自己的答案时,我才知道。 这真是太搞笑了! 例外是昂贵的 @AndrewScottEvans 您可能正在考虑其他语言。 Python 不运行本机机器代码,因此没有必要将异常实现为硬件中断。在 C API 中,异常是 NULL 返回值和全局设置(每个线程)异常对象:docs.python.org/3/c-api/exceptions.html 可以说,动态类型虚拟机的整个概念本身就是性能损失,但鉴于此,没有理由使 Python 异常从根本上比任何其他 Python 语句更慢或更快。【参考方案3】:

实现您自己的列表索引?

class mylist(list):
  def index_withoutexception(self,i):
    try:
        return self.index(i)
    except:
        return -1

因此,您可以使用列表,并使用您的 index2,在出现错误时返回您想要的内容。

你可以这样使用它:

  l = mylist([1,2,3,4,5]) # This is the only difference with a real list
  l.append(4) # l is a list.
  l.index_withoutexception(19) # return -1 or what you want

【讨论】:

请注意,这可能会破坏某些代码:type(l) == list 在这里是 False 另外 - 我并不积极,但在我看来,如果目标是避免引发异常(如果经常发生这种情况会很昂贵),那么这并没有实现。它将返回 -1,但在内部仍会引发异常,但代价仍然很高。【参考方案4】:

编写一个满足你需要的函数:

def find_in_iterable(x, iterable):
    for i, item in enumerate(iterable):
        if item == x:
            return i
    return None

如果只需要知道item是否存在,而不需要知道索引,可以使用in

x in yourlist

【讨论】:

谢谢-但我希望得到一些预先包装好的东西 PS '-1' 是一个有效的列表索引 - 您需要返回 'None' @Yarin:我选择 -1 的原因是为了与现有的 Python 习惯用法保持一致,例如 'abc'.find('x') == -1,但 None 也可以。我会更新我的答案。 是的,奇怪的是 .find() 这样做 - 似乎与 Python 的负索引不一致。【参考方案5】:

是的,有。你可以例如。做类似的事情:

test = lambda l, e: l.index(e) if e in l else None

这样工作:

>>> a = ['a', 'b', 'c', 'g', 'c']
>>> test(a, 'b')
1
>>> test(a, 'c')
2
>>> test(a, 't')
None

所以,基本上,test() 将返回给定列表(第一个参数)中元素的索引(第二个参数),除非它没有被发现(在在这种情况下,它将返回 None,但它可以是您认为合适的任何东西。

【讨论】:

【参考方案6】:

如果你不关心它在序列中的位置,只关心它的存在,那么使用in 运算符。否则,编写一个重构异常处理的函数。

def inlist(needle, haystack):
  try:
    return haystack.index(needle)
  except ...:
    return -1

【讨论】:

我发布了一个答案,使用in 检查是否存在并最终返回索引,但我看到您知道该方法并决定使用处理可能引发的异常。我的问题是:你为什么选择捕获异常而不是首先检查haystack 是否存在needle?有什么理由吗? @Tadeck:因为不成文的 Python 规则,“请求原谅比请求许可更容易”。 谢谢 :) 我刚刚在 Python 词汇表上找到了它,其中 EAFP (Easier to Ask for Forgiveness than for Permission) 显示为与 LBYL (Look Before You Leap) 相反的东西。确实,EAFP 已被命名为常见的 Python 编码风格,所以它并不是那么不成文:) 再次感谢! 您实际上并不需要 except ...: 行中的 ...【参考方案7】:

我喜欢使用Web2py's List 类,在其gluon 包的存储模块中找到。 storage 模块提供类列表 (List) 和类字典 (Storage) 数据结构,在找不到元素时不会引发错误。

首先下载web2py's source,然后将gluon包文件夹复制粘贴到你的python安装的站点包中。

现在试试吧:

>>> from gluon.storage import List
>>> L = List(['a','b','c'])
>>> print L(2)
c
>>> print L(3) #No IndexError!
None

注意,它也可以像普通列表一样工作:

>>> print L[3]

Traceback (most recent call last):
File "<pyshell#4>", line 1, in <module>
l[3]
IndexError: list index out of range

【讨论】:

好主意,谢谢。)def __call__(self, idx, df=None): return self[idx] if 0&lt;=idx&lt;len(self) else df【参考方案8】:

没有内置的方法可以做你想做的事。

这是一篇可以帮助你的好帖子:Why list doesn't have safe "get" method like dictionary?

【讨论】:

那篇文章是关于数组索引的,有IndexError: list index out of range 问题,而不是这里描述的index() 方法和ValueError: &lt;value&gt; is not in list 问题。【参考方案9】:

希望对你有帮助

lst= ','.join('qwerty').split(',') # create list
i='a'  #srch string
lst.index(i) if i in lst else None

【讨论】:

以上是关于Python 的 list.index() 函数,当没有找到时不会抛出异常的主要内容,如果未能解决你的问题,请参考以下文章

python列表的内建函数

python常用列表函数

Python 中 list.index(x) 的复杂性

Python3的index()方法

python list 操作

学习python的第十七天(二分法,有名函数,匿名函数,内置函数)