“foo is None” 和 “foo == None” 之间有什么区别吗?

230

这两者有什么不同吗:

if foo is None: pass

if foo == None: pass

我看到大多数Python代码(包括我自己编写的代码)遵循前者的约定,但最近我遇到了使用后者的代码。 None是NoneType的实例(也是唯一的实例,如果我没记错的话),所以这应该没有关系,对吗?是否存在任何可能影响结果的情况?


这个回答解决了你的问题吗?"is None"和"== None"有什么区别 - Trilarion
12个回答

273

is如果比较的是同一个对象实例,总是返回True,而==则由__eq__()方法决定。

>>> class Foo:
        def __eq__(self, other):
            return True

>>> f = Foo()
>>> f == None
True
>>> f is None
False

60
你可能想补充说明,None是一个单例对象,因此“None is None”始终为True。 - Bite code
52
你可能需要补充说明,is 运算符无法被用户定义的类重载。 - martineau
1
@study 方法__eq__(self)是一个特殊的内置方法,用于确定在Python对象上使用==时如何处理。在这里,我们已经重写了它,以便当在类型为Foo的对象上使用==时,它总是返回true。对于is运算符没有等效的方法,因此无法以同样的方式更改is的行为。 - Brendan
是因为foo类的定义没有构造函数,即__init__函数吗? - study

51
你可能想要阅读这篇文章:对象的身份和等价性is语句用于判断对象的身份,它检查对象是否引用同一个实例(在内存中的相同地址)。
==语句则表示相等性(相同的值)。

嗯,我想你的链接变了,除非你对如何从Python调用外部函数感兴趣。 - Pat
我刚刚尝试了以下代码: a=1;b=1;print(a is b) # True。你有什么想法,为什么即使它们似乎是两个不同的对象(在内存中具有不同的地址),a is b 的结果也为真? - Johnny Chiu

28
请注意:
一句警告:
if foo:
    # do something

不是完全一样的。
if foo is not None:
    # do something

前者是一个布尔值测试,可以在不同的上下文中评估为false。有许多事物在布尔值测试中代表false,例如空容器、布尔值。在这种情况下,None也会评估为false,但其他事物也会如此。

12

foo is None 是首选的原因在于,您可能正在处理定义了自己的 __eq__ ,并将对象定义为等于 None 的对象。因此,如果需要查看它是否实际上是 None,始终使用 foo is None


12
(ob1 is ob2)等于(id(ob1) == id(ob2))

7
但是(ob是ob2)要快得多。 Timeit显示"(a is b)"每个循环需要0.0365微秒,而"(id(a)==id(b))"需要0.153微秒。快了4.2倍! - AKX
4
is 版本不需要函数调用,也不需要任何 Python 解释器属性查找;如果 ob1 实际上就是 ob2,解释器可以立即回答。 - u0b34a0f6ae
18
不,它并不是这样。"{}为{}"是错误的,而"id({}) == id({})"可以是(在CPython中确实如此)。请参见https://dev59.com/i2865IYBdhLWcg3wTc3h。 - Piotr Dobrogost

10

没有区别,因为相同的对象自然会相等。然而,PEP 8明确规定你应该使用 is:

与单例对象(如None)的比较应该始终使用is或is not,永远不要使用等于操作符。


9

is 用于测试身份(identity),而不是相等(equality)。对于语句foo is none,Python只比较对象的内存地址。这意味着你正在问问题 "我是否有两个名称指向同一个对象?"

另一方面,==测试等式,由 __eq__() 方法确定。它不关心身份(identity)。

In [102]: x, y, z = 2, 2, 2.0

In [103]: id(x), id(y), id(z)
Out[103]: (38641984, 38641984, 48420880)

In [104]: x is y
Out[104]: True

In [105]: x == y
Out[105]: True

In [106]: x is z
Out[106]: False

In [107]: x == z
Out[107]: True

None是一个单例操作符。因此None is None总是为真。

In [101]: None is None
Out[101]: True

5

对于None来说,等于(==)和恒等(is)之间不应该有差别。NoneType类型可能会在等于操作中返回恒等。由于None是唯一的可以创建NoneType实例的对象(我认为是这样的),所以这两个操作是相同的。但对于其他类型来说,情况并非总是如此。例如:

list1 = [1, 2, 3]
list2 = [1, 2, 3]
if list1==list2: print "Equal"
if list1 is list2: print "Same"

这将打印“相等”,因为列表具有比较运算,不是默认返回身份。

4

@Jason:

I recommend using something more along the lines of

if foo:
    #foo isn't None
else:
    #foo is None

我不喜欢使用 "if foo:",除非 foo 真的表示布尔值(即 0 或 1)。如果 foo 是字符串、对象或其他什么东西,"if foo:" 可能会起作用,但在我看来,它看起来像是一种懒惰的捷径。如果你想检查 x 是否为 None,请说 "if x is None:"。


使用“if var”的方式检查空字符串/列表是首选方法。布尔转换定义良好,而且代码更简洁,性能表现也更好。例如,“if len(mylist) == 0”没有理由这么做。 - mthurlin
错误。假设foo = ""。那么,if foo将返回false,注释#foo is None是错误的。 - blokeley
2
请注意给打分的用户 - 我的答案是引用了一个已被删除的回答并对其表示了不同意见。如果你_不喜欢_我的代码,请用_upvote_来表示你的看法。 :-) - Graeme Perrow

3

更多细节:

  1. is子句实际上检查两个object是否位于同一内存位置。也就是说,它们是否指向相同的内存位置并具有相同的id

  2. 由于1的影响,is可以确保两个在词法上表示的object是否具有相同的属性(属性-属性...)或不具有相同的属性。

  3. 原始类型的实例化,例如boolintstring(有一些例外)、NoneType具有相同的值,将始终位于相同的内存位置。

例如:

>>> int(1) is int(1)
True
>>> str("abcd") is str("abcd")
True
>>> bool(1) is bool(2)
True
>>> bool(0) is bool(0)
True
>>> bool(0)
False
>>> bool(1)
True

由于NoneType在Python的“查找”表中只能有一个实例,因此前者和后者更多地是代码编写者的编程风格(可能是为了保持一致性),而不是选择其中一个的微妙逻辑原因。


3
所有阅读此文的人:绝对不要使用 some_string is "bar" 来比较字符串。没有任何可以接受的理由这样做,并且当你不期望时会出现错误。它通常能正常工作只是因为 CPython 知道创建两个具有相同内容的不可变对象是愚蠢的。但这种情况仍然可能发生。 - ThiefMaster
@ThiefMaster 这个答案是否容易被误解?不过,我再读一遍后并没有发现有问题(提到了“一些例外”)。你的陈述只适用于字符串而不是 int,对吗? - Bleeding Fingers
不是真的,但既然你在回答中提到了这个例子,我想警告用户实际使用它是一个坏主意。也许在那行代码后面加上类似于“# cpython特定/不保证”的东西会更好…… - ThiefMaster

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接