了解Python的“ is”运算符


109

is运营商不匹配变量的值,但这些实例本身。

到底是什么意思

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

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

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

print(x is y)  # It prints false!

Answers:


181

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

is操作员文档中:

运算符isis not对象标识测试:x is y当且仅当xy是相同对象时为true 。

改用==运算符:

print(x == y)

打印Truex并且y是两个单独的列表:

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

如果使用该id()函数,您将看到xy具有不同的标识符:

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

但是如果要分配给yx则它们都指向同一个对象:

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

is显示两者是同一个对象,则返回True

请记住,在Python中,名称只是引用值的标签;您可以有多个名称指向同一个对象。is告诉您两个名称是否指向一个相同的对象。==告诉您两个名称是否引用具有相同值的对象。


13
因此,A is B与相同id(A) == id(B)
imallett

2
@imallett:这是同一测试的代理,只要您不存储id(A)在变量中,以后希望variable == id(B)它仍然可以工作;如果A在此期间被删除,则B可以被赋予相同的存储位置。
马丁·彼得斯

1
无法格式化注释。但是有一件有趣的事情。:) >>> x = 5 \n>>> y = 5 \n>>> x是y \nTrue \n>>> x == y \nTrue \n>>>\n
Haranadh

5
小整数在CPython中被内插,因为它们经常使用。这是一个优化。x = 5; y = 5; x是y =>真,因为id(x)== id(y)。这是重复使用的相同整数对象。由于整数是不可变的,因此可在Python中工作。如果x = 1.0; y = 1.0或x = 9999; y = 9999,它不会是相同的标识,因为不对float和较大的int进行插值。
MagnusLyckå17年

1
@MagnusLyckå还有其他优化可以导致不可变对象被缓存。例如,如果您在新函数中运行示例,或者在交互式解释器中与分号一起运行,您会发现它们也具有相同的ID。
马丁·皮特斯

60

另一个重复是在问为什么两个相等的字符串通常不相同,在这里并没有真正回答:

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

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

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

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

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

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

1
出色的解释,尤其是最后的建议。
DavidG

感谢您的详细说明。有人知道吗:如果wz由于编译器的折叠值而相同,为什么即使在使用REPL时也是如此,即使id()在检查引用时也是如此?在Python 3.7上使用REPL
Chi-chi

8

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]

8

重复的问题提示,此类推可能会起作用:

# - 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

3
可能只是个人口味(没有双关语),但我发现这种类比比帮助更令人困惑,并且让我想在冰箱里没东西的时候吃布丁。可能更具启发性
汤姆·

1
@TomClose:关于这个问题有很多好的答案,足够有余地。另外,我也要布丁。
阿玛丹(Amadan)

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
>>>

上面的例子显示,你的身份(也可以在CPython的内存地址)是两个不同的ab(即使它们的值是相同的)。这就是为什么当您说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
>>>

希望有帮助。


这是一个很好的例子。感谢您提供详细信息。
Haranadh17年

但请尝试a = 123456789 b = 123456789
user2183078

小于-5或高于256Python的所有内容均为False。Python会缓存[-5,256]范围内的数字。
聪明的

正如您所显示的,并非所有不可变对象都会被共享,这是Python运行时针对某些对象而不是其他对象进行的优化。共享小整数的过程已被详细记录,但我认为这不是用于字符串实习
Mark Ransom

4

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


不,不是。在大多数情况下,它的行为可能相似,但并非总是如此。看到此内容 -页面的最底部,项目符号6 ::>(...),您可能会注意到在is运算符的某些用法中看似不正常的行为,例如涉及实例方法或常量之间的比较以及最小工作示例:`class A(object):def foo(self):通过a = A()print a.foo是a.foo print id(a.foo)== id(a.foo)`
Tomasz Kurgan


2

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


5
Python没有指针。您需要收紧术语。
David Heffernan

3
它在内部运行,就像Java和许多其他语言一样。实际上,is操作员的功能可以显示这一点。
Neko 2012年

5
实施细节并不重要。该文档使用术语“对象标识”。你也应该这样。“运算符是否测试对象身份,并且不进行测试:如果且仅当x和y是同一对象时,x为y才为真。x不是y会产生反真值。”
David Heffernan

1
@Neko:CPython在内部使用指针。但是显然Jython(用Java实现)和PyPy(用Python子集实现)不使用指针。在PyPy中,id除非您要求,否则某些对象甚至都不会具有。
abarnert 2014年

1

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


1

一个简单的例子,水果

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 ) )

-3

is运营商不过是英文版的==。由于两个列表的ID不同,因此答案为假。你可以试试:

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

*因为两个列表的ID都相同


is是不是“的英文版本==
大卫·巴克
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.