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

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

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


简介:

使用是当你想检查对象的标识(例如检查var是否为None)。当你想检查是否相等时使用==(例如var是否等于3?)

解释:

你可以自定义my_var == None返回True的类

e.g:

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

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

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

换句话说,==是等价性检查(从对象到对象定义),而是对象同一性检查:

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这样的单例对象的比较总是应该使用is或is 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(参见上面的代码)。如果我使用的不是none,代码正常工作。


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

请看下面的if和else。当数据库返回“None”时,只有“is”有效。如果用==代替,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

True

a = 1.25
b = 1.25
a is b

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

a = 256
b = 256
a is b

True

a = 257
b = 257
a is b

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

Edit: Joooeey makes the good point that which integers are stored in memory is specific to your python implementation, and the example of numbers from -5 to 256 is specific to CPython. If you don't know what you're running, it's probably CPython, which is the most common implementation. But for this reason (and others) it is better practice to compare equality between these numbers with a == 2 and not with a is 2. As for None, it is specified to be the sole instance of the NoneType type according to the Python Documentation itself, so regardless of implementation you can always compare it using a is None.