为什么 str.find 在没有找到子字符串时返回 -1?

Oli*_*çon 6 python string

在另一个询问如何在不使用 str.split 的情况下获取字符串的第一个单词的问题中,一些用户提出了以下想法。

word = s[:s.find(' ')]
Run Code Online (Sandbox Code Playgroud)

只要字符串s包含空格,它就会起作用。尽管如果它不包含一个,这会默默地失败。

s = 'foo'
word = s[:s.find(' ')] # 'fo'
Run Code Online (Sandbox Code Playgroud)

这是因为当未找到子字符串时str.find返回。-1

由于以下原因,这似乎是一个奇怪的设计选择。

  1. -1实际上是一个正确的索引

  2. 解释为 a bool,它在上下文中没有任何有用的含义

  3. None似乎是一个更好的候选者(并且将使上面的代码工作)

是否有一个好的设计原因或用例为什么-1选择作为返回值str.find而不是None当没有找到子字符串时?

Arn*_*Arn 1

感谢您提出这个问题,因为我很惊讶查找示例的展示没有提到这个问题。我同意你的推理,但我一直无法为如此重要的实施决定找到具体的理由。

如果您查看find 方法及其最重要的实现组件fastsearch,您将看到它-1在无法找到子字符串时返回,如文档中所述。然而,代码中没有任何迹象解释为什么要这样编写。有趣的是,其中一条评论包括有关该返回值的警告,表明开发人员意识到这个细节是一个怪癖。

为什么这是一个问题?

想象一下,您有一些字符串,其中包含基于小写无空格公司名称的 id inc,这些 id 可能以 结尾,您希望将其删除以对其进行规范化。

在典型场景中,您可以执行以下操作:

s = "fastcareinc"
idx = s.find("inc")
>>> s[:idx]
'fastcare'
Run Code Online (Sandbox Code Playgroud)

但是,如果名称已经采用规范化形式,则最终会遇到严重错误,导致 id 的错误表示:

s = "fastcare"
idx = s.find("inc")
>>> s[:idx]
'fastcar'
Run Code Online (Sandbox Code Playgroud)

上面,名称已从 更改为"fastcare""fastcar"因为变量idx-1,因此切片会删除 中的最后一个字符s。如果str.find返回None,那就不是问题了:

s = "fastcare"
idx = None
>>> s[:idx]
'fastcare'
Run Code Online (Sandbox Code Playgroud)

解决此问题的最佳方法是通过 cPython 的 PR 在 Python 社区中表达它。不过,目前我建议您使用str.indexin代替str.find来避免在生产代码中出现这种情况。或者,我根据概述的场景提出一种解决方法,可以让您远离该错误:

>>> s[:None if (idx := s.find("inc")) == -1 else idx]
'fastcare'
Run Code Online (Sandbox Code Playgroud)