为什么“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。 我错过了什么吗?
我只是发现,从我的个人域名过滤脚本失败。
is
validation对象的身份和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
的返回值和x
和y
是同一个对象,因为它有相同的id()
)。 当然,你也不应该依赖这个 – 优化器不必自动实现任何东西,它只是一个优化启发式; 如果你需要intern
string,明确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