python-3.x 比较字符串和空格时,“is”运算符的行为不同

sq1bmfud  于 2023-01-14  发布在  Python
关注(0)|答案(6)|浏览(135)

我已经开始学习Python(python 3.3),我尝试了is操作符。

>>> b = 'is it the space?'
>>> a = 'is it the space?'
>>> a is b
False
>>> c = 'isitthespace'
>>> d = 'isitthespace'
>>> c is d
True
>>> e = 'isitthespace?'
>>> f = 'isitthespace?'
>>> e is f
False

看起来空格和问号使is的行为有所不同,这是怎么回事?

**编辑:**我知道我应该使用==,我只是想知道为什么is会这样。

wmtdaxz3

wmtdaxz31#

  • 警告:这个答案是关于一个特定python解释器的实现细节。用is ==比较字符串是个坏主意。*

好吧,至少对于cpython3.4/2.7.3来说,答案是"不,这不是空白"。

  • 如果两个字符串文字是字母数字或驻留在同一 * 块 *(文件、函数、类或单个解释器命令)上,则它们将共享内存
  • 当且仅当计算结果为字符串的表达式是使用常量和二元/一元运算符创建的,并且生成的字符串短于21个字符时,该表达式才会生成与使用字符串文本创建的对象相同的对象。
  • 单个字符是唯一的。

示例

字母数字字符串常量总是共享内存:

>>> x='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
>>> y='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
>>> x is y
True

非字母数字字符串常量共享内存当且仅当它们共享封闭的语法块:
(口译员)

>>> x='`!@#$%^&*() \][=-. >:"?<a'; y='`!@#$%^&*() \][=-. >:"?<a';
>>> z='`!@#$%^&*() \][=-. >:"?<a';
>>> x is y
True 
>>> x is z
False

(文件)

x='`!@#$%^&*() \][=-. >:"?<a';
y='`!@#$%^&*() \][=-. >:"?<a';
z=(lambda : '`!@#$%^&*() \][=-. >:"?<a')()
print(x is y)
print(x is z)

输出:TrueFalse
对于简单的二进制运算,编译器会执行非常简单的常量传播(参见peephole.c),但对于字符串,只有当结果字符串短于21个字符时,编译器才会执行常量传播。如果是这种情况,前面提到的规则将生效:

>>> 'a'*10+'a'*10 is 'a'*20
True
>>> 'a'*21 is 'a'*21
False
>>> 'aaaaaaaaaaaaaaaaaaaaa' is 'aaaaaaaa' + 'aaaaaaaaaaaaa'
False
>>> t=2; 'a'*t is 'aa'
False
>>> 'a'.__add__('a') is 'aa'
False
>>> x='a' ; x+='a'; x is 'aa'
False

当然,单个字符总是共享内存:

>>> chr(0x20) is ' '
True
doinxwow

doinxwow2#

进一步扩展一下伊格纳西奥的回答:is运算符是恒等运算符,用于比较 object identity,如果构造两个内容相同的对象,那么通常不会出现对象identity为true的情况,它适用于一些小字符串,因为Python的参考实现CPython将 contents 分开存储。使所有这些对象引用相同的字符串内容,所以is操作符对这些对象返回true。
然而,这是CPython的一个实现细节,通常对CPython或任何其他实现都没有保证,所以使用这个事实是一个坏主意,因为它可能在任何其他时间中断。
要比较字符串,请使用==运算符来比较对象的 equality。当两个字符串对象包含相同的字符时,它们被视为相等。因此,这是比较字符串时使用的正确运算符,并且如果您不 * 显式地想要 * object identity,通常应避免使用is(例如:a is False)。
如果你真的对细节感兴趣,你可以找到CPython的字符串here的实现。这是实现细节,所以您应该 * 永远 * 不要求它工作。

x3naxklr

x3naxklr3#

is操作符依赖于id函数,即guaranteed to be unique among simultaneously existing objects.。具体地说,id返回对象的内存地址。对于只包含字符a-z和A-Z的字符串,CPython似乎具有一致的内存地址。
然而,这似乎只有在字符串被赋给变量时才是这样:
这里,“foo”的id和a的id相同。在检查id之前,a已被设置为“foo”。

>>> a = "foo"
>>> id(a)
4322269384
>>> id("foo")
4322269384

然而,当在将a设置为等于“bar”之前检查“bar”的id时,“bar”的id和a的id是不同的.

>>> id("bar")
4322269224
>>> a = "bar"
>>> id(a)
4322268984
  • 在 * 将a设置为等于“bar”之后,再次检查“bar”的id,返回相同的id。
>>> id("bar")
4322268984

因此,当那些字符串被赋值给变量时,cPython似乎为只包含a-zA-Z的字符串保持了一致的内存地址,这也完全可能是版本相关的:我在macbook上运行python 2.7.3,其他人可能会得到完全不同的结果。

jvidinwx

jvidinwx4#

实际上,你的代码相当于比较对象id(即它们的物理地址),所以不用比较is:

>>> b = 'is it the space?'
>>> a = 'is it the space?'
>>> a is b
False

您可以:

>>> id(a) == id(b)
False

但是,请注意,如果a和b直接进行比较,它将工作。

>>> id('is it the space?') == id('is it the space?')
True

实际上,在表达式中,相同的静态字符串之间是共享的,但是在程序级别上,只有类似单词的字符串才共享(因此既没有空格也没有标点符号)。
您不应该依赖于这种行为,因为它没有在任何地方进行文档记录,并且是实现的细节。

ttcibm8c

ttcibm8c5#

两个或多个相同的连续字母数字字符串(只有)字符存储在一个结构中,因此它们共享它们的内存引用。自20世纪90年代以来,互联网上到处都有关于这种现象的帖子。显然一直都是这样。我从未见过一个合理的猜测,为什么会出现这种情况。我只知道它是。此外,如果你拆分并重新连接字母数字字符串以去除单词之间的空格,那么得到的相同字母数字字符串不共享引用,我觉得这很奇怪。
向这两个字符串添加任何相同的非字母数字值,它们立即成为副本,但不是共享引用。

a ="abbacca";  b = "abbacca";  a is b => True
a ="abbacca "; b = "abbacca "; a is b => False
a ="abbacca?"; b = "abbacca?"; a is b => False

~ C博士

uxhixvfz

uxhixvfz6#

“is”运算符比较实际对象。
c is d也应该是false。我的猜测是python做了一些优化,在这种情况下,它是同一个对象。

相关问题