31

我已经开始学习 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会这样。

4

5 回答 5

26

警告:这个答案是关于特定 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
于 2013-05-26T08:22:09.067 回答
16

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

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

要比较字符串,请使用比较对象相等==性的运算符。当两个字符串对象包含相同的字符时,它们被认为是相等的。所以这是比较字符串时使用的正确运算符,如果您不明确需要对象标识(例如:),通常应该避免使用。isa is False


如果你真的对细节感兴趣,你可以在这里找到 CPython 字符串的实现。但同样:这是实现细节,所以你永远不应该要求它工作。

于 2013-05-26T06:18:46.870 回答
4

is操作符依赖于id函数,guaranteed to be unique among simultaneously existing objects. 具体来说,就是返回对象的id内存地址。对于仅包含字符 az 和 AZ 的字符串,CPython 似乎具有一致的内存地址。

但是,这似乎只在字符串被分配给变量时才会出现:

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

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

但是,在a设置为“bar”之前检查“bar”的id时,“bar”的id和“bar”的id是不同的a

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

设置等于“bar”再次检查“bar ”的id返回相同的id。a

>>> id("bar")
4322268984

因此,当将这些字符串分配给变量时,cPython 似乎为仅包含 a-zA-Z 的字符串保持一致的内存地址。这也完全有可能取决于版本:我在 macbook 上运行 python 2.7.3。其他人可能会得到完全不同的结果。

于 2013-05-26T06:59:22.043 回答
1

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

事实上,在一个表达式中,相同的静态字符串之间存在共享。但是,在程序规模上,只共享类似单词的字符串(因此既不是空格也不是标点符号)。

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

于 2013-05-26T06:29:58.683 回答
-1

'is' 运算符比较实际对象。

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

于 2013-05-26T06:18:47.690 回答