【问题标题】:"IN" operator with empty strings in Python 3.0 [duplicate]Python 3.0中带有空字符串的“IN”运算符[重复]
【发布时间】:2026-02-15 15:35:02
【问题描述】:

在阅读 Python 3 教程时,我遇到了以下问题:

>>> '' in 'spam'
True

我的理解是'' 等于没有空格。

当我尝试以下 shell 终端时,我得到如下所示的输出:

>>> '' in ' spam '
True

有人可以帮忙解释一下发生了什么吗?

【问题讨论】:

    标签: python string python-3.x


    【解决方案1】:

    '' 为空字符串,与"" 相同。空字符串是 every 其他字符串的子字符串。

    ab 是字符串时,表达式a in b 会检查ab 的子字符串。即a的字符序列必须存在于b中;必须有一个索引i 使得b[i:i+len(a)] == a。如果a 为空,则任何索引i 都满足此条件。

    这并不意味着当你遍历b 时,你会得到a。与其他序列不同,虽然for a in b 生成的每个元素都满足a in b,但a in b 并不意味着a 将通过迭代b 来生成。

    所以'' in x"" in x 对任何字符串x 都返回True:

    >>> '' in 'spam'
    True
    >>> "" in 'spam'
    True
    >>> "" in ''
    True
    >>> '' in ""
    True
    >>> '' in ''
    True
    >>> '' in ' ' 
    True
    >>> "" in " "
    True
    

    【讨论】:

    • 如果空字符串,根据定义,必须存在于其他字符串中,为什么它不是迭代器集的一部分? IE。对于“垃圾邮件”中的 i:打印(i)
    • @Brightlights 这是一个有趣的问题。我的措辞可能不正确——本质上,b 中的a(对于字符串)检查a 的所有元素是否在b 中。因此,如果a 为空,则它的每个元素(不是元素)都存在于任何b 中。请参阅我的更新答案。
    • @RushyPanchal:这不是检查的工作方式。 a in b for strings 检查 ab 的子字符串。要使检查评估为True,必须有一些索引i 使得b[i:i+len(a)] == a。 (这与所有其他内置序列类型完全不同。)
    • @Brightlights 字符串迭代器迭代每个 1 字符的子字符串(即每个字符)。空字符串不是字符串的字符。但是,in 运算符,即包含检查,仅检查字符串 a 是否作为子字符串包含在 b 中。您可以在字符串的每个零长度子字符串中找到空字符串,因此 '' in x 对每个字符串 x 都是正确的。
    • @Brightlights: for i in 'spam': print(i) 也不会打印'am',即使'am' 是'spam' 的子字符串。那是因为for i in 'spam' 不会遍历所有子字符串,它会遍历所有字符。如果您以某种方式遍历所有子字符串,它确实会包含''
    【解决方案2】:

    string literal'' 表示空字符串。这基本上是一个长度为零的字符串,不包含任何字符。

    in 运算符被定义为for sequences,如果s 中的一项等于x,则返回“True,否则False”用于表达式x in s。对于一般序列,这意味着s 中的一项(通常使用迭代可访问)等于测试元素x。然而,对于字符串,in 运算符具有 subsequence 语义。所以x in s 为真,当xs 的子字符串时。

    形式上,这意味着对于长度为n 的子字符串x,必须有一个满足以下表达式的索引is[i:i+n] == x

    这个例子很容易理解:

    >>> s = 'foobar'
    
    >>> x = 'foo'
    >>> n = len(x) # 3
    >>> i = 0
    >>> s[i:i+n] == x
    True
    
    >>> x = 'obar'
    >>> n = len(x) # 4
    >>> i = 2
    >>> s[i:i+n] == x
    True
    

    从算法上讲,in 运算符(或底层的__contains__ 方法)需要做的是将i 迭代到所有可能的值(0 <= i < len(s) - n)并检查任何i 的条件是否为真.

    回顾空字符串,很清楚为什么'' in s 检查每个字符串都为真sn 为零,所以我们检查s[i:i];这就是每个有效索引 i 的空字符串本身:

    >>> s[0:0]
    ''
    >>> s[1:1]
    ''
    >>> s[2:2]
    ''
    

    s 本身就是空字符串也是如此,因为序列切片被定义为在指定序列之外的范围时返回一个空序列(这就是为什么你可以对短字符串执行s[74565463:74565469])。

    这就解释了为什么在检查空字符串作为子字符串时,使用in 进行的包含检查总是返回True。但即使你从逻辑上考虑它,你也可以看到原因:一个子字符串是一个字符串的一部分,你可以在另一个字符串中找到它。然而,空字符串可以在 between 每两个字符之间找到。就像你可以在一个数字上添加无限数量的零,你可以在一个字符串中添加无限数量的空字符串,而无需实际修改该字符串。

    【讨论】:

    • 很好的答案,值得@顶。
    【解决方案3】:

    正如 Rushy Panchal 所指出的,in 包含运算符遵循集合论约定,并假设空字符串是任何字符串的子字符串。

    您可以尝试通过考虑以下内容来说服自己为什么这是有意义的:让s 成为一个字符串,使得'' in s == False。那么'' in s[len(s):] 最好通过传递性为假(否则s 的一个子集包含'',但s 不包含'' 等)。但是然后'' in '' == False,这也不是很好。所以你不能选择任何字符串s,这样'' not in s就不会产生问题。

    当然,如有疑问,请模拟一下:

    s = input('Enter any string you dare:\n')
    
    print('' in '')
    print(s == s + '' == '' + s)
    print('' in '' + s)
    

    【讨论】: