当字符串中有点时,为什么“is”关键字具有不同的行为?

时间:2010-05-18 15:18:57

标签: python identity equality

考虑以下代码:

>>> x = "google"
>>> x is "google"
True
>>> x = "google.com"
>>> x is "google.com"
False
>>>

为什么会这样?

为了确保上述内容正确,我刚刚在Linux上测试了Python 2.5.4,2.6.5,2.7b2,Python 3.1和Linux上的Python 2.7b1。

看起来所有这些都是一致的,所以它是设计的。我错过了什么吗?

我只是发现我的一些个人域名过滤脚本失败了。

2 个答案:

答案 0 :(得分:90)

is验证对象标识,并且当它遇到不可变类型的文字时,任何Python的实现完全可以 创建该不可变类型的新对象,寻找该类型的现有对象,以查看是否可以重用其中一些(通过添加对同一底层对象的新引用)。这是一种实用的优化选择,而受语义约束的影响,因此您的代码永远不应该依赖于实现可能采用的路径(或者它可能会破坏Python的错误修复/优化版本!)

考虑例如:

>>> import dis
>>> def f():
...   x = 'google.com'
...   return x is 'google.com'
... 
>>> dis.dis(f)
  2           0 LOAD_CONST               1 ('google.com')
              3 STORE_FAST               0 (x)

  3           6 LOAD_FAST                0 (x)
              9 LOAD_CONST               1 ('google.com')
             12 COMPARE_OP               8 (is)
             15 RETURN_VALUE    

所以在这个特定的实现中,在一个函数中,你的观察不适用,只有一个对象是为文字(任何文字),而且确实:

>>> f()
True

实际上是因为在一个函数中,通过本地常量表传递(通过不创建多个常量不可变对象来节省一些内存),这是非常便宜和快速的,并且可能提供良好的性能返回,因为该函数可能之后反复调用。

但是,相同的实现,在交互式提示编辑:我原本以为这也会发生在模块的顶层,但@Thomas的评论让我说对了,见后文):

>>> x = 'google.com'
>>> y = 'google.com'
>>> id(x), id(y)
(4213000, 4290864)

不打算尝试以这种方式保存内存 - id是不同的,即不同的对象。可能会有更高的成本和更低的回报,因此该实现的优化器的启发式方法告诉它不要费心搜索并继续进行。

编辑:在模块顶层,根据@Thomas的观察,例如:

$ cat aaa.py
x = 'google.com'
y = 'google.com'
print id(x), id(y)

我们再次在此实现中看到基于常量表的内存优化:

>>> import aaa
4291104 4291104

(编辑@Thomas观察结束)。

最后,同样的实施:

>>> x = 'google'
>>> y = 'google'
>>> id(x), id(y)
(2484672, 2484672)

启发式在这里是不同的,因为文字字符串“看起来可能是一个标识符” - 所以它可能在需要实习的操作中使用...所以优化器无论如何实习(并且一旦实习,寻找它就变成了当然非常快)。事实上,惊喜......:

>>> z = intern(x)
>>> id(z)
2484672
第一次

... x intern(如您所见,intern的返回值相同对象为xy,因为它具有相同的id())。当然,您也不应该依赖于此 - 优化器不会 自动实施任何内容,它只是一种优化启发式方法;如果你需要intern ed字符串,intern明确,为了安全起见。明确地执行实习生字符串时......:

>>> x = intern('google.com')
>>> y = intern('google.com')
>>> id(x), id(y)
(4213000, 4213000)

...然后您执行确保每次都使用完全相同的对象(即相同的id()) - 因此您可以应用微优化,例如使用{进行检查{1}}而不是is(我几乎没有发现微不足道的性能提升值得打扰; - )。

编辑:只是为了澄清,这是我正在谈论的那种性能差异,在缓慢的Macbook Air上......:

==

...最多只有几十纳秒。所以,甚至值得思考只是在最极端的“优化[expletive deleted]中解决这个[expletive deleted]性能瓶颈”的情况! - )

答案 1 :(得分:15)

“是”是身份测试。 Python对小整数和(显然)字符串有一些缓存行为。 “is”最适合单身人士测试(例如None)。

>>> x = "google"
>>> x is "google"
True
>>> id(x)
32553984L
>>> id("google")
32553984L
>>> x = "google.com"
>>> x is "google.com"
False
>>> id(x)
32649320L
>>> id("google.com")
37787888L