Python无比较:我应该使用"is"还是= = ?

我的编辑器在比较my_var == None时警告我,但在使用my_var is None时没有警告。

我在Python shell中做了一个测试,确定两者都是有效的语法,但我的编辑器似乎说my_var is None是首选。

是这样吗?如果是的话,为什么?

183644 次浏览

简介:

当你想检查对象的身份时,使用is(例如,检查var是否为None)。当你想检查平等时使用==(例如:var是否等于3?)

解释:

你可以自定义类,其中my_var == None将返回True

例句:

class Negator(object):
def __eq__(self,other):
return not other


thing = Negator()
print thing == None    #True
print thing is None    #False

is检查对象身份。只有一个对象None,所以当你执行my_var is None时,你是在检查它们是否实际上是同一个对象(而不仅仅是等效对象)

换句话说,==检查等价性(从对象到对象定义),而is检查对象身份:

lst = [1,2,3]
lst == lst[:]  # This is True since the lists are "equivalent"
lst is lst[:]  # This is False since they're actually different objects

在比较任意对象和像None这样的单例对象时,通常首选is,因为它更快,更可预测。is总是根据对象标识符进行比较,而==将做什么取决于操作数的确切类型,甚至取决于它们的顺序。

这个建议得到了PEP 8的支持,明确状态表示“对None这样的单例对象的比较总是应该用isis not来完成,而不是相等操作符。”

PEP 8定义了比较单例对象时最好使用is操作符。

我最近遇到了这可能出错的地方。

import numpy as np
nparray = np.arange(4)


# Works
def foo_is(x=None):
if x is not None:
print(x[1])


foo_is()
foo_is(nparray)


# Code below raises
# ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
def foo_eq(x=None):
if x != None:
print(x[1])


foo_eq()
foo_eq(nparray)

我创建了一个函数,该函数可选地接受numpy数组作为参数,如果包含numpy数组则更改。如果我使用不等式运算符!=测试它是否包含,这将引发ValueError(参见上面的代码)。如果我使用is not none,代码正常工作。

我们可以以列表为例。看这里:

a = list('Hello')
b = a
c = a[:]

都有相同的值:

['H', 'e', 'l', 'l', 'o']
['H', 'e', 'l', 'l', 'o']
['H', 'e', 'l', 'l', 'o']

is指的是实际的内存槽,不管它们是否是感兴趣的特定对象:

print(a is b)
print(a is c)

现在我们得到了想要的结果。

True
False

PEP 8也提到了这一点,说“对像None这样的单例对象的比较总是应该使用is或is not,而不是相等操作符。”

is也更快。

另一个例子是"=="与“;is”不同。当您从数据库中提取信息并检查值是否存在时,结果将是值或None。

请看下面的if和else。只有“女儿家;当数据库返回"None"时生效。如果用==代替,If语句将不起作用,它将直接转到else,即使结果是"None"。希望我讲清楚了。

conn = sqlite3.connect('test.db')
c = conn.cursor()
row = itemID_box.get()


# pull data to be logged so that the deletion is recorded
query = "SELECT itemID, item, description FROM items WHERE itemID LIKE '%" + row + "%'"
c.execute(query)
result = c.fetchone()


if result is None:
# log the deletion in the app.log file
logging = logger('Error')
logging.info(f'The deletion of {row} failed.')
messagebox.showwarning("Warning", "The record number is invalid")
else:
# execute the deletion
c.execute("DELETE from items WHERE itemID = " + row)
itemID_box.delete(0, tk.END)
messagebox.showinfo("Warning", "The record has been deleted")
conn.commit()
conn.close()

有助于增进人们的理解。

使用None检查身份的原因是因为Python只将None的值存储在内存中的一个位置,而每个等于None的对象都将其值存储在相同的位置。有一些“特殊价值”;而None只是其中之一。

但是大多数值都没有这种特殊待遇!例如,浮动1.25可以存储在内存中的不同位置:

a = None
b = None
a is b

真正的

a = 1.25
b = 1.25
a is b

None恰好是少数始终存储在内存中某个位置的值之一。另一个例子是-5256之间的任何整数…由于这些整数经常被使用,它们总是存储在内存中,并且每个具有该值的整数都存储在计算机内存中的相同位置!试试吧:

a = 256
b = 256
a is b

真正的

a = 257
b = 257
a is b

所以你可以认为None是一个特殊的值类的一部分,它总是有一个固定的内存地址。这就是为什么我们可以使用is来检查两个变量是否都是None…它只是检查内存地址是否相同。

编辑:Joooeey提出了一个很好的观点,即哪些整数存储在内存中是特定于你的python实现的,并且从-5256的数字示例是特定于CPython的。如果你不知道你在运行什么,它可能是CPython,这是最常见的实现。但出于这个原因(和其他原因),更好的做法是比较这些数字与a == 2a is 2之间的相等性。至于None,根据Python文档本身,它被指定为NoneType类型的唯一实例,所以无论实现如何,你总是可以使用a is None进行比较。