【问题标题】:Confused about `is` operator with strings对带有字符串的 `is` 运算符感到困惑
【发布时间】:2019-08-18 09:16:39
【问题描述】:

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算子的输出不一致?

【问题讨论】:

  • 实际上你可能永远不会使用is,除非你正在做一些相当复杂的事情。对于大多数相等比较,您可能需要==
  • @dkamins:嗯,推荐的测试None 的方法使用'is':some_var is None(因为总是只有一个None 实例) .我会说这是一个很常见的情况。
  • @voithos True - 我一直在使用它(和is not None),甚至没有想到它!但除此之外......
  • @dkamins 它也常用于TrueFalse,因为它们也是单例。
  • @voithos 确实,但是我发现自己很少使用这些成语。 if somethingif not something 在我看来通常比 if something is Trueif something is False 更清晰。

标签: python string


【解决方案1】:

请注意,这是 CPython 特定的优化。如果你希望你的代码是可移植的,你应该避免它。例如,在 PyPy 中

>>>> a = "hi"
>>>> b = "hi"
>>>> a is b
False

同样值得指出的是,小整数也会发生类似的事情

>>> a = 12
>>> b = 12
>>> a is b
True

您也不应该依赖它,因为其他实现可能不包括此优化。

【讨论】:

    【解决方案2】:

    我相信它与string interning. 有关本质上,这个想法是只存储每个不同字符串的一个副本,以提高某些操作的性能。

    基本上,a is b 起作用的原因是(您可能已经猜到)在这两种情况下,Python 都引用了一个不可变的字符串。当字符串很大时(很可能还有一些我不理解的其他因素),这不会完成,这就是您的第二个示例返回 False 的原因。

    编辑:事实上,奇怪的行为似乎是交互环境的副作用。如果您将相同的代码放入 Python 脚本中,a is bktr is 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 is b 仍然返回True,也许分配的字符串足够小,可以快速搜索interned 集合,而另一个不是?

    【讨论】:

    • 关于a = "p" + "oi"; b = "po" + "i",编译器通过在代码对象中存储两个对"poi"的引用来优化简单的字符串连接,这两个引用在原地实习。 "today is a fine day" 没有被留存,因为它包含空格(即非名称字符)。
    【解决方案3】:

    is 是身份测试。它适用于 smaller 一些字符串(因为缓存),但不适用于 bigger 其他字符串。由于 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
    

    注意strptr 的ID 不同。

    但是:

    >>> x = "poi"
    >>> y = "poi"
    >>> id(x)
    26650592
    >>> id(y)
    26650592
    #hence this comparison returns true : x is y
    

    x 和 y 的 ID 相同。因此,is 运算符适用于“ids”而不是“equalities”

    请参阅下面的链接,了解有关 python 何时以及为什么会为相同的字符串分配不同的内存位置的讨论(也请阅读问题)。

    When does python allocate new memory for identical strings

    此外,python3.x 上的 sys.intern 和 python2.x 上的 intern 应该可以帮助您将字符串分配到相同的内存位置,而不管字符串的大小。

    【讨论】:

    • 一个字符串可以有多小/
    • 你可以有一个空字符串""。这还不是全部,因为可以将两个字符串设置为相同的值,这样is 返回true,然后您可以修改一个并撤消修改,结果不会是true any更多。
    • 你当然可以有比这个实习生更大的字符串。虽然短字符串是正确的,而较大的字符串不是,但“它适用于较短的字符串”并不是全部答案。
    • 与字符串大小无关。如果字符串常量(不是来自表达式)都是“名称字符”,则在创建代码对象时它将被实习。加上用于变量名、属性等的实现字符串。
    • 参见 codeobject.c(2.7.3 源代码)中的 herehere
    【解决方案4】:

    is == 相同。

    基本上,is 检查两个对象是否相同,而== 比较这些对象的值(字符串,就像 python 中的所有内容一样,都是对象)。

    所以当你真的知道你在看什么对象时,你应该使用is(即你已经制作了这些对象,或者正在与None进行比较,正如问题cmets指出的那样),并且你想要知道两个变量是否引用了内存中的完全相同的对象

    但是,在您的示例中,您正在查看 python 在幕后处理的 str 对象,因此如果不深入了解 python 的工作原理,您真的不知道会发生什么。 ints 或 floats 也会有同样的问题。其他答案很好地解释了“幕后”的东西(字符串实习),但您在日常编程中大多不必担心它。

    【讨论】:

    • 那么为什么这两个例子不同呢?这不解释了。
    • @agf 刚刚编辑 - 这是否解释了我的意思是我的答案更好?
    猜你喜欢
    • 2011-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多