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