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

YOU*_*YOU 56 python identity equality

考虑以下代码:

>>> x = "google"
>>> x is "google"
True
>>> x = "google.com"
>>> x is "google.com"
False
>>>
Run Code Online (Sandbox Code Playgroud)

为什么会那样?

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

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

我发现,我的一些个人域名过滤脚本失败了.

Ale*_*lli 90

is验证对象的身份,和Python,当遇到字面不变类型的任何实现,是完全自由要么做出不可变类型的新对象,通过该类型的现有对象觅见,如果他们中的一些可重复使用(通过添加对同一底层对象的新引用).这是优化的一个务实的选择并没有受到语义约束,所以你的代码不应该依赖于该路径的给予实施可能要(或者它可能与Python的bug修正/优化版本破门!).

考虑例如:

>>> 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    
Run Code Online (Sandbox Code Playgroud)

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

>>> f()
True
Run Code Online (Sandbox Code Playgroud)

实际上,这是因为在一个函数中,通过本地常量表(通过不创建多个常量不可变对象来保存一些内存)是相当便宜和快速的,并且可以提供良好的性能返回,因为该函数可能被重复调用然后.

但是,相同的实现,在交互式提示符下(编辑:我原本以为这也会发生在模块的顶层,但@Thomas的评论让我正确,见后面):

>>> x = 'google.com'
>>> y = 'google.com'
>>> id(x), id(y)
(4213000, 4290864)
Run Code Online (Sandbox Code Playgroud)

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

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

$ cat aaa.py
x = 'google.com'
y = 'google.com'
print id(x), id(y)
Run Code Online (Sandbox Code Playgroud)

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

>>> import aaa
4291104 4291104
Run Code Online (Sandbox Code Playgroud)

(根据@Thomas的观察结束编辑).

最后,同样的实施:

>>> x = 'google'
>>> y = 'google'
>>> id(x), id(y)
(2484672, 2484672)
Run Code Online (Sandbox Code Playgroud)

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

>>> z = intern(x)
>>> id(z)
2484672
Run Code Online (Sandbox Code Playgroud)

... x 已经intern第一次编辑(如你所见,返回值intern是和,因为它具有相同的对象).当然,你不应该依赖于这个要么-优化并不具备自动实习生什么,它只是一个优化启发式; 如果你需要ed字符串,他们明确,只是为了安全.当你明确地实习生字符串时...:xyid()internintern

>>> x = intern('google.com')
>>> y = intern('google.com')
>>> id(x), id(y)
(4213000, 4213000)
Run Code Online (Sandbox Code Playgroud)

...然后你确保每次确保完全相同的对象(即相同id()) - 所以你可以应用微优化,例如检查is而不是==(我几乎没有发现微小的性能增益值得麻烦;-).

编辑:只是为了澄清,这是我正在谈论的那种性能差异,在缓慢的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
Run Code Online (Sandbox Code Playgroud)

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

  • 看起来我在误用**是**操作员 (8认同)
  • 亚历克斯,你说"在模块的顶层(或交互式提示)",但我相信你所描述的(以及OP看到的)*仅*在交互式提示下发生 - 模块的顶层仍被编译成一个单个代码对象,并且该代码对象中对同一常量的所有引用都使用相同的引用. (2认同)

Jer*_*own 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
Run Code Online (Sandbox Code Playgroud)