将string与空格进行比较时,“是”运算符的行为不同

我已经开始学习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行为。

警告:这个答案是关于特定的Python解释器的实现细节。 比较stringis ==糟糕的主意。

那么,至less对于cpython3.4 / 2.7.3来说,答案是“不,它不是空白”。 不仅是空白:

  • 两个string文字将共享内存,如果它们是字母数字或驻留在同一个 (文件,函数,类或单个解释器命令)

  • 当且仅当使用常量和二元/一元运算符创build的expression式的值为一个string时,才会产生与使用string文字创build的对象相同的对象,并且结果string的长度小于21个字符。

  • 单个字符是唯一的。

例子

字母数字string文字总是共享内存:

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

非字母数字string文字共享内存当且仅当它们共享封闭的语法块:

(解释程序)

 >>> 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 ),但是对于string,只有当结果string短于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 

为了进一步扩大Ignacio的答案: is运算符是身份运算符。 它用来比较对象的身份。 如果构造两个具有相同内容的对象,则通常情况下不是对象标识生成为真。 它适用于一些小的string,因为Python的参考实现CPython分开存储内容 ,使所有这些对象引用相同的string内容。 所以is运算符为那些返回true。

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

要比较string,可以使用==运算符来比较对象的相等性。 两个string对象包含相同的字符时被认为是相等的。 所以这是比较string时使用的正确的运算符,如果你不明确地想要对象标识 (例如: a is False ),通常应该避免使用它。


如果你真的对细节感兴趣,你可以在这里findCPythonstring的实现。 但是,这是实现细节,所以你不应该要求这个工作。

is运算符依赖于id函数, guaranteed to be unique among simultaneously existing objects. 具体而言, id返回对象的内存地址。 看来CPython对于只包含字符az和AZ的string具有一致的内存地址。

但是,这似乎只是当string已被分配给一个variables的情况下:

这里,“foo”的id和a的id是一样的。 a在检查id之前已经被设置为“foo”。

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

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

 >>> id("bar") 4322269224 >>> a = "bar" >>> id(a) 4322268984 

设置等于“bar” 之后,再次检查“bar”的ID将返回相同的ID。

 >>> id("bar") 4322268984 

所以看起来,cPython为那些只包含a-zA-Z的string保留了一致的内存地址,当这些string被分配给一个variables的时候。 这也是完全可能的,这是版本相关的:我在MacBook上运行Python 2.7.3。 其他人可能得到完全不同的结果

事实上,你的代码等于比较对象的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 

实际上,在一个expression式中,在相同的静态string之间共享。 但是,在节目规模上,只能共享类似字的string(既不是空格也不是标点符号)。

你不应该依赖这个行为,因为它没有被logging在任何地方,并且是实现的细节。

'是'运算符比较实际的对象。

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

Interesting Posts