将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)
输出: True
和False
对于简单的二进制操作,编译器正在做非常简单的常量传播(参见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做了一些优化,在这种情况下,它是同一个对象。