【问题标题】:Understanding Python's "is" operator理解 Python 的“is”运算符
【发布时间】:2012-11-18 23:21:42
【问题描述】:

is 运算符与变量的值不匹配,但 实例本身。

这到底是什么意思?

我声明了两个名为 xy 的变量,在这两个变量中分配了相同的值,但是当我使用 is 运算符时它返回 false。

我需要澄清一下。这是我的代码。

x = [1, 2, 3]
y = [1, 2, 3]

print(x is y)  # It prints false!

【问题讨论】:

标签: python operators


【解决方案1】:

您误解了 is 运算符的测试内容。它测试两个变量是否指向相同的对象,而不是两个变量是否具有相同的值。

来自is operator 的文档:

运算符isis not 测试对象身份:当且仅当xy 是同一个对象时,x is y 为真。

改用== 运算符:

print(x == y)

这将打印Truexy 是两个单独的列表:

x[0] = 4
print(y)  # prints [1, 2, 3]
print(x == y)   # prints False

如果您使用id() function,您会看到xy 具有不同的标识符:

>>> id(x)
4401064560
>>> id(y)
4401098192

但如果您要将y 分配给x,那么两者都指向同一个对象:

>>> x = y
>>> id(x)
4401064560
>>> id(y)
4401064560
>>> x is y
True

is表明两者是同一个对象,它返回True

请记住,在 Python 中,names are just labels referencing values;您可以有多个名称指向同一个对象。 is 告诉您两个名称是否指向同一个对象。 == 告诉您两个名称是否指代具有相同值的对象。

【讨论】:

  • 所以,A is Bid(A) == id(B) 相同。
  • @imallett:这是同一测试的代理,前提是您不将 id(A) 存储在变量中,然后期望 variable == id(B) 仍然有效;如果A同时被删除,那么B可以被赋予相同的内存位置。
  • 有道理,也是正确的; variable 正在存储以前存在的东西的属性。运行时无法检测到后面的使用是错误的。标准的关键部分是“[id() ]保证对于此对象在其生命周期内是唯一且恒定的。具有非重叠生命周期的两个对象可能具有相同的 id() 值。 "
【解决方案2】:

Another duplicate 询问为什么两个相等的字符串通常不相同,这里没有真正回答:

>>> x = 'a' 
>>> x += 'bc'
>>> y = 'abc'
>>> x == y
True
>>> x is y
False

那么,为什么它们不是同一个字符串?特别是考虑到这一点:

>>> z = 'abc'
>>> w = 'abc'
>>> z is w
True

让我们先推迟第二部分。第一个怎么可能是真的?

解释器必须有一个“实习表”,一个将字符串值映射到字符串对象的表,因此每次您尝试使用内容'abc' 创建一个新字符串时,都会返回相同的对象。 Wikipedia 对实习的工作方式进行了更详细的讨论。

而Python一个字符串实习表;您可以使用 sys.intern 方法手动实习字符串。

事实上,允许 Python 自动实习任何不可变类型,但要求 这样做。不同的实现会产生不同的值。

CPython(如果你不知道你正在使用哪个实现,你正在使用的实现)自动实习小整数和一些特殊的单例,如False,但不是字符串(或大整数,或小元组,或其他任何东西)。你可以很容易地看到这一点:

>>> a = 0
>>> a += 1
>>> b = 1
>>> a is b
True
>>> a = False
>>> a = not a
>>> b = True
a is b
True
>>> a = 1000
>>> a += 1
>>> b = 1001
>>> a is b
False

好的,但为什么 zw 相同?

这不是解释器自动实习,而是编译器折叠值。

如果相同的编译时字符串在同一个模块中出现两次(这究竟意味着什么很难定义——它与字符串文字不同,因为r'abc''abc''a' 'b' 'c' 是所有不同的文字但相同的字符串——但易于直观理解),编译器只会创建一个字符串实例,有两个引用。

事实上,编译器可以走得更远:'ab' + 'c' 可以被优化器转换为'abc',在这种情况下,它可以与同一模块中的'abc' 常量一起折叠。

同样,这是 Python 允许但不是必须做的事情。但在这种情况下,CPython 总是折叠小字符串(以及,例如,小元组)。 (尽管交互式解释器的逐语句编译器运行的优化与一次模块编译器不同,因此您不会以交互方式看到完全相同的结果。)


那么,作为程序员,你应该怎么做呢?

嗯……没什么。您几乎没有任何理由关心两个不可变值是否相同。如果您想知道何时可以使用a is b 而不是a == b,那么您问错了问题。始终使用a == b,除非有两种情况:

  • 要与x is None 等单例值进行更具可读性的比较。
  • 对于可变值,当你需要知道改变x是否会影响y时。

【讨论】:

  • 感谢您的详细解释。有人知道:如果 wz 由于编译器折叠值而相同,为什么这在 REPL 中也有效,即使使用 id() 检查引用?在 Python 3.7 上使用 REPL
【解决方案3】:

is 仅在它们实际上是同一个对象时才返回 true。如果它们相同,则对其中一个的更改也会出现在另一个中。这是一个不同的例子。

>>> x = [1, 2, 3]
>>> y = [1, 2, 3]
>>> print x is y
False
>>> z = y
>>> print y is z
True
>>> print x is z
False
>>> y[0] = 5
>>> print z
[5, 2, 3]

【讨论】:

    【解决方案4】:

    duplicate question 提示,这个类比可能有效:

    # - Darling, I want some pudding!
    # - There is some in the fridge.
    
    pudding_to_eat = fridge_pudding
    pudding_to_eat is fridge_pudding
    # => True
    
    # - Honey, what's with all the dirty dishes?
    # - I wanted to eat pudding so I made some. Sorry about the mess, Darling.
    # - But there was already some in the fridge.
    
    pudding_to_eat = make_pudding(ingredients)
    pudding_to_eat is fridge_pudding
    # => False
    

    【讨论】:

    • 可能只是个人口味(不是双关语),但我发现这个类比更令人困惑而不是有用,当我冰箱里没有布丁时,我想吃布丁:(我认为马克Ransom 的回答虽然比较无聊,但可能更有启发性
    • @TomClose:关于这个问题有很多很好的答案,足以让空间变得轻浮。另外,我也想要布丁。
    【解决方案5】:

    isis not 是 Python 中的两个身份运算符。 is 运算符不比较变量的值,而是比较变量的标识。考虑一下:

    >>> a = [1,2,3]
    >>> b = [1,2,3]
    >>> hex(id(a))
    '0x1079b1440'
    >>> hex(id(b))
    '0x107960878'
    >>> a is b
    False
    >>> a == b
    True
    >>>
    

    上面的例子告诉你,ab 的标识(也可以是 Cpython 中的内存地址)是不同的(即使它们的值相同)。这就是为什么当您说a is b 时,由于两个操作数的身份不匹配,它会返回 false。但是,当您说a == b 时,它会返回true,因为== 操作仅验证两个操作数是否具有相同的分配给它们的值。

    有趣的例子(额外成绩):

    >>> del a
    >>> del b
    >>> a = 132
    >>> b = 132
    >>> hex(id(a))
    '0x7faa2b609738'
    >>> hex(id(b))
    '0x7faa2b609738'
    >>> a is b
    True
    >>> a == b
    True
    >>>
    

    在上面的例子中,即使ab 是两个不同的变量,a is b 返回了True。这是因为a 的类型是int,它是一个不可变对象。所以python(我想是为了节省内存)在使用相同的值创建b时分配了相同的对象。所以在这种情况下,匹配和a is b的变量的身份原来是True

    这将适用于所有不可变对象:

    >>> del a
    >>> del b
    >>> a = "asd"
    >>> b = "asd"
    >>> hex(id(a))
    '0x1079b05a8'
    >>> hex(id(b))
    '0x1079b05a8'
    >>> a is b
    True
    >>> a == b
    True
    >>>
    

    希望对您有所帮助。

    【讨论】:

    • 但是试试 a=123456789 b=123456789
    • Python 中小于-5 或大于256 的所有内容都将为False。 Python 缓存 [-5, 256] 范围内的数字。
    • 并非所有不可变对象都会像您展示的那样共享,这是 Python 运行时针对某些对象而不是其他对象应用的优化。共享小整数的过程有据可查,但我不认为它适用于string interning
    【解决方案6】:

    x is yid(x) == id(y) 相同,比较对象的身份。

    正如@tomasz-kurgan 在下面的评论中指出的is 运算符对某些对象的行为异常。

    例如

    >>> class A(object):
    ...   def foo(self):
    ...     pass
    ... 
    >>> a = A()
    >>> a.foo is a.foo
    False
    >>> id(a.foo) == id(a.foo)
    True
    

    参考;
    https://docs.python.org/2/reference/expressions.html#is-not
    https://docs.python.org/2/reference/expressions.html#id24

    【讨论】:

    • 不,它没有。在大多数情况下,它的行为可能相似,但并非总是如此。请参阅this - 页面的最底部,第 6 条。:> (...),您可能会注意到在某些使用 is 运算符时看似不寻常的行为,例如涉及实例之间比较的那些方法或常量以及最小的工作示例:` class A(object): def foo(self): pass a = A() print a.foo is a.foo print id(a.foo) == id(a.富) `
    【解决方案7】:

    你可以在这里检查一个小整数。 257以上的数字都不是小整数,所以算作不同的对象。

    在这种情况下最好使用==

    更多信息在这里:http://docs.python.org/2/c-api/int.html

    【讨论】:

      【解决方案8】:

      X 指向一个数组,Y 指向另一个数组。这些数组是相同的,但is 运算符会查看那些不完全相同的指针。

      【讨论】:

      • Python 没有指针。你需要收紧你的术语。
      • 它在内部执行,就像 Java 和许多其他语言一样。事实上,is 运算符的功能显示了这一点。
      • 实现细节并不重要。该文档使用术语“对象身份”。你也应该这样。 “运算符是和不是对象身份的测试:当且仅当 x 和 y 是同一个对象时,x 是 y 为真。x 不是 y 产生逆真值。”
      • @Neko:CPython 内部使用指针。但显然 Jython(用 Java 实现)和 PyPy(用 Python 的一个子集实现)不使用指针。在 PyPy 中,一些对象甚至没有id,除非你要求它。
      【解决方案9】:

      它比较对象身份,即变量是否引用内存中的同一个对象。这就像 Java 或 C 中的 ==(比较指针时)。

      【讨论】:

        【解决方案10】:

        一个简单的水果例子

        fruitlist = [" apple ", " banana ", " cherry ", " durian "]
        newfruitlist = fruitlist
        verynewfruitlist = fruitlist [:]
        print ( fruitlist is newfruitlist )
        print ( fruitlist is verynewfruitlist )
        print ( newfruitlist is verynewfruitlist )
        

        输出:

        True
        False
        False
        

        如果你尝试

        fruitlist = [" apple ", " banana ", " cherry ", " durian "]
        newfruitlist = fruitlist
        verynewfruitlist = fruitlist [:]
        print ( fruitlist == newfruitlist )
        print ( fruitlist == verynewfruitlist )
        print ( newfruitlist == verynewfruitlist )
        

        输出不同:

        True
        True
        True
        

        这是因为 == 运算符只比较变量的内容。要比较 2 个变量的身份,请使用 is 运算符

        要打印识别号:

        print ( id( variable ) )
        

        【讨论】:

          【解决方案11】:

          is 运算符只不过是== 的英文版。 因为这两个列表的 ID 不同,所以答案是错误的。 你可以试试:

          a=[1,2,3]
          b=a
          print(b is a )#True
          

          *因为两个列表的 ID 相同

          【讨论】:

          • is 不是“== 的英文版”
          猜你喜欢
          • 2022-11-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-12-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多