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

lui*_*iel 29 python operators object-identity python-3.x

我开始学习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
Run Code Online (Sandbox Code Playgroud)

似乎空间和问号使is行为表现不同.这是怎么回事?

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

Ela*_*zar 22

警告:这个答案是关于特定python解释器的实现细节.将字符串与is==坏主意进行比较.

好吧,至少对于cpython3.4/2.7.3,答案是"不,它不是空白".不只是空白:

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

  • 求值为字符串的表达式将导致一个对象与使用字符串文字创建的对象相同,当且仅当它是使用常量和二进制/一元运算符创建时,结果字符串短于21个字符.

  • 单个字符是唯一的.

例子

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

>>> x='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
>>> y='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
>>> x is y
True
Run Code Online (Sandbox Code Playgroud)

当且仅当它们共享封闭的语法块时,非字母数字字符串文字才共享内存:

(解释程序)

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

(文件)

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

输出: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
Run Code Online (Sandbox Code Playgroud)

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

>>> chr(0x20) is ' '
True
Run Code Online (Sandbox Code Playgroud)


pok*_*oke 16

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

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

要比较字符串,可以使用==比较对象相等性的运算符.当两个字符串对象包含相同的字符时,它们被视为相等.因此,这是在比较字符串时使用的正确运算符,is如果您没有明确地想要对象标识,则通常应该避免使用(例如:) a is False.


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

  • `a is False`是美丽的英语.显然真的太好了:) (5认同)
  • "a是假的"毫无意义.正确的拼写是'不是'. (3认同)

Nol*_*lty 5

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

然而,这似乎只有当字符串被分配给变量时才会出现:

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

>>> a = "foo"
>>> id(a)
4322269384
>>> id("foo")
4322269384
Run Code Online (Sandbox Code Playgroud)

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

>>> id("bar")
4322269224
>>> a = "bar"
>>> id(a)
4322268984
Run Code Online (Sandbox Code Playgroud)

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

>>> id("bar")
4322268984
Run Code Online (Sandbox Code Playgroud)

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