比较字符串和空格时,'is'运算符的行为不同

时间:2013-05-26 06:09:39

标签: python python-3.x operators object-identity

我开始学习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的行为如此。

5 个答案:

答案 0 :(得分:20)

警告:这个答案是关于特定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

答案 1 :(得分:16)

稍微扩展一下Ignacio的答案:is运算符是身份运算符。它用于比较对象身份。如果构造具有相同内容的两个对象,则通常不会出现对象标识为true的情况。它适用于一些小字符串,因为CPython(Python的参考实现)分别存储内容,使所有这些对象引用相同的字符串内容。因此is运算符为那些返回true。

然而,这是CPython的实现细节,并且通常既不保证CPython也不保证任何其他实现。所以使用这个事实是一个坏主意,因为它可能会破坏其他任何一天。

要比较字符串,可以使用==运算符来比较对象的相等。当两个字符串对象包含相同的字符时,它们被视为相等。因此,这是在比较字符串时使用的正确运算符,如果您明确地想要 ,则通常应避免使用is(例如:{{1 }})。


如果您对细节非常感兴趣,可以找到CPython的字符串here的实现。但同样:这是实现细节,因此您永远不会要求它工作。

答案 2 :(得分:4)

is运算符依赖于id函数,即guaranteed to be unique among simultaneously existing objects.具体而言,id返回对象的内存地址。似乎CPython对只包含字符a-z和A-Z的字符串具有一致的内存地址。

但是,这似乎只是在将字符串分配给变量时的情况:

这里,“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。其他人可能会得到完全不同的结果。

答案 3 :(得分:0)

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

>>> 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

实际上,在表达式中,相同的静态字符串之间存在共享。但是,在程序规模上,只有字样的字符串共享(所以既不是空格也不是标点符号)。

您不应该依赖此行为,因为它没有在任何地方记录,并且是实施的细节。

答案 4 :(得分:-1)

&#39;是&#39;运算符比较实际对象。

c is d也应该是假的。我的猜测是python进行了一些优化,在这种情况下,它是同一个对象。