Python 混淆带字符串的'is'运算符

Python 混淆带字符串的'is'运算符,python,string,Python,String,is操作符比较两个对象的内存地址,如果它们相同,则返回True。那么,为什么它不能可靠地处理字符串呢? 代码#1 >>> a = "poi" >>> b = "poi" >>> a is b True 代码#2 >>> ktr = "today is a fine day" >>> ptr = "today is a fine day" >>> ktr is ptr False 我创建

is
操作符比较两个对象的内存地址,如果它们相同,则返回
True
。那么,为什么它不能可靠地处理字符串呢? 代码#1

>>> a = "poi"
>>> b = "poi"
>>> a is b
True
代码#2

>>> ktr = "today is a fine day"
>>> ptr = "today is a fine day"
>>> ktr is ptr
False

我创建了两个字符串,它们的内容相同,但它们位于不同的内存地址上。为什么
运算符的输出不一致?

是身份测试。它适用于较小的一些字符串(因为缓存),但不适用于较大的其他字符串。因为str不是ptr。[谢谢你,埃里克森]

请参阅此代码:

>>> import dis
>>> def fun():
...   str = 'today is a fine day'
...   ptr = 'today is a fine day'
...   return (str is ptr)
...
>>> dis.dis(fun)
  2           0 LOAD_CONST               1 ('today is a fine day')
              3 STORE_FAST               0 (str)

  3           6 LOAD_CONST               1 ('today is a fine day')
              9 STORE_FAST               1 (ptr)

  4          12 LOAD_FAST                0 (str)
             15 LOAD_FAST                1 (ptr)
             18 COMPARE_OP               8 (is)
             21 RETURN_VALUE

>>> id(str)
26652288
>>> id(ptr)
27604736
#hence this comparison returns false: ptr is str
注意
str
ptr
的ID是不同的

但是:

x和y的ID相同。因此,
is
运算符在“id”上工作,而不是在“equalities”上工作

请参阅下面的链接,了解python何时以及为什么会为相同字符串分配不同的内存位置的讨论(也请阅读问题)

另外,python3.x上的
sys.intern
和python2.x上的
intern
应该可以帮助您在相同的内存位置分配字符串,而不管字符串的大小

=
不同

基本上,
is
检查两个对象是否相同,而
=
比较这些对象的值(字符串,就像python中的所有东西一样,都是对象)

因此,当你真正知道你在看什么对象时,你应该使用
is
(如问题注释所指出的,你已经制作了对象,或者正在与
None
进行比较),并且你想知道两个变量是否引用了内存中完全相同的对象


然而,在您的示例中,您看到的是python在幕后处理的
str
对象,因此如果不深入了解python的工作原理,您就不知道会发生什么。
int
s或
float
s也会有同样的问题。其他答案很好地解释了“幕后”的东西(字符串实习),但在日常编程中,您基本上不必担心它。

我认为这与本质有关,其思想是只存储每个不同字符串的一个副本,以提高某些操作的性能

基本上,
a是b
之所以有效,是因为(正如您可能猜到的)在这两种情况下Python都引用了一个不可变的字符串。当一个字符串很大时(很可能还有其他一些我不理解的因素),就不会这样做,这就是第二个示例返回False的原因

编辑:事实上,这种奇怪的行为似乎是交互环境的副作用。如果将相同的代码放入Python脚本中,
a是b
ktr是ptr
都返回True

a="poi"
b="poi"
print a is b  # Prints 'True'

ktr = "today is a fine day"
ptr = "today is a fine day"
print ktr is ptr  # Prints 'True'
这很有意义,因为Python很容易解析源文件并在其中查找重复的字符串文本。如果动态创建字符串,则即使在脚本中,其行为也会有所不同

a="p" + "oi"
b="po" + "i"
print a is b  # Oddly enough, prints 'True'

ktr = "today is" + " a fine day"
ptr = "today is a f" + "ine day"
print ktr is ptr  # Prints 'False'

至于为什么
a是b
的结果仍然为True,也许分配的字符串足够小,可以通过interned collection进行快速搜索,而另一个不是这样?

请注意,这是一个特定于CPython的优化。如果你希望你的代码是可移植的,你应该避免它。例如,在PyPy中

>>>> a = "hi"
>>>> b = "hi"
>>>> a is b
False
同样值得指出的是,小整数也会发生类似的情况

>>> a = 12
>>> b = 12
>>> a is b
True

您也不应该依赖于此,因为其他实现可能不包括此优化

就这一点而言,一个字符串可以有多小/您可以有一个空字符串
”。但这并不是全部,因为两个字符串可以设置为相同的值,因此
is
返回
true
,然后您可以修改其中一个并撤消修改,结果将不再是
true
。您当然可以拥有比这个更大的字符串。虽然短字符串是正确的,而大字符串不是,但“它与短字符串一起工作”并不是全部答案。它与字符串的大小无关。如果字符串常量(不是来自表达式)都是“名称字符”,则在创建代码对象时会将其插入。加上用于变量名、属性等的实现字符串。请参阅codeobject.c(2.7.3源代码)中的和。那么为什么这两个示例不同呢?这并不能解释。@agf刚刚编辑过-这能解释我的答案是什么意思吗?实际上,除非你做的事情相当复杂,否则你可能永远不会使用
is
。对于大多数相等性比较,您可能需要
==
。@dkamins:好吧,测试
使用的推荐方法是:
一些变量是无
(因为总是只有一个
的实例)。我得说这是一个非常常见的情况。@voithos-True-我一直在使用它(而且
不是无
),甚至没有想到它!但除此之外…@dkamins它也常用于
True
False
,因为它们也是单身汉。@voithos确实如此,但我发现自己很少使用这些成语
如果某件事
如果不是某件事
通常比
如果某件事是真的
如果某件事是假的
对我来说更清楚;b=“po”+“i”
,编译器通过在代码对象中存储对
“poi”
的两个引用来优化简单的字符串连接,这两个引用被固定在适当的位置<代码>“今天天气很好”
不实习,因为它包含空格(即非姓名字符)。
>>> a = 12
>>> b = 12
>>> a is b
True