为什么“is”关键字在string中有一个点时会有不同的行为?

考虑这个代码:

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

为什么这样?

为了确保以上是正确的,我刚刚在Python上testing了Python 2.5.4,2.6.5,2.7b2,Python 3.1和Linux上的Python 2.7b1。

看起来所有人都有一致性,所以这是devise。 我错过了什么吗?

我只是发现,从我的个人域名过滤脚本失败。

isvalidation对象的身份和Python的任何实现,当它满足不变types的字面量时,完全可以自由地创build该不可变types的新对象, 或者通过该types的现有对象寻找是否可以重用(通过添加一个新的引用到相同的基础对象)。 这是一个优化的实用select,不受语义限制的影响,所以你的代码不应该依赖于实现可能采用的path(或者它可能会破坏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是不同的,即不同的对象。 有潜在的更高的成本和更低的回报,所以这个实现的优化器的启发式告诉它不要打扰search,只是继续前进。

编辑 :在模块顶层,按@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) 

启发式在这里是不同的,因为字面string“看起来可能是一个标识符” – 所以它可能在操作中需要interning使用…所以优化器无论如何实施(并且一旦实习,寻找它变得非常快课程)。 确实,惊喜…:

 >>> z = intern(x) >>> id(z) 2484672 

x 已经第一次intern了(就像你看到的, intern的返回值和xy同一个对象,因为它有相同的id() )。 当然,你也不应该依赖这个 – 优化器不必自动实现任何东西,它只是一个优化启发式; 如果你需要internstring,明确intern ,只是为了安全。 当你明确的实习生string…:

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

…那么你确实确保完全相同的对象(即,相同的id() )结果每一次 – 所以你可以应用微优化,如检查与而不是== (我几乎没有find性能增益微不足道,值得费心;-)。

编辑 :只是为了澄清,这里是我谈论的性能差异,在一个缓慢的Macbook Air …:

 $ python -mtimeit -s"a='google';b='google'" 'a==b' 10000000 loops, best of 3: 0.132 usec per loop $ python -mtimeit -s"a='google';b='google'" 'a is b' 10000000 loops, best of 3: 0.107 usec per loop $ python -mtimeit -s"a='goo.gle';b='goo.gle'" 'a==b' 10000000 loops, best of 3: 0.132 usec per loop $ python -mtimeit -s"a='google';b='google'" 'a is b' 10000000 loops, best of 3: 0.106 usec per loop $ python -mtimeit -s"a=intern('goo.gle');b=intern('goo.gle')" 'a is b' 10000000 loops, best of 3: 0.0966 usec per loop $ python -mtimeit -s"a=intern('goo.gle');b=intern('goo.gle')" 'a == b' 10000000 loops, best of 3: 0.126 usec per loop 

最多只要几十纳秒。 所以,甚至只是在最极端的情况下才会考虑 “优化[expletive deleted]出这个[expletive deleted]性能瓶颈的情况!”)

“是”是身份testing。 Python对于小整数和(显然)string都有一些caching行为。 “是”最适合单身testing(例如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