Python中的空对象

1467
9个回答

1932

在Python中,“null”对象是单例None

要检查某个东西是否为None,请使用is身份运算符:

if foo is None:
    ...

147
选择 egg is None 而不是 egg == None 的原因是:后者可以被重载,当将有效对象与 None 进行比较时很可能会出错(这取决于它的实现方式,但你不能期望每个人都考虑到与 None 的比较),而 is 始终按照相同的方式工作。 - user395760
108
为什么 Python 的设计者们不选择使用"null"呢?非要与众不同吗! ;) - Vidar
41
@Vidar 'None'不是缺少对象(就像'null'是一种空引用),它是一个实际的对象。你永远不会得到一个'None'对象,它可以代替任何类型为'NoneType'以外的对象。它也不是一个语言概念。它不是Python语言内置的,它是Python标准库的一部分。 None !== (呵呵) Null。 - Rushyo
13
如果没有指针的概念,那么这样做没有任何意义。如果您正在使用ctypes库,则None将用作地址零的同义词,但仍然没有语言概念的“null引用”。在Python中,即使对象为空(None),您仍然会引用某种对象。我认为这极大地简化了语言。 - Rushyo
7
这是一份精彩的演示,解释了“十亿美元错误”的空引用问题:http://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare。除了空引用之外,其他选项包括Options或Maybe(自定义)类型。 - Michahell
显示剩余5条评论

253

None, Python的空值?

Python中没有null,而是使用None来表示。如前所述,检测某个值是否为None的最准确方法是使用is运算符,该运算符测试两个变量是否引用同一个对象。

>>> foo is None
True
>>> foo = 'bar'
>>> foo is None
False

基础知识

None是唯一的

None是类NoneType的唯一实例。任何进一步试图实例化该类的尝试将返回相同的对象,这使得None成为单例。初学Python的人经常会看到提到NoneType的错误消息,并想知道它是什么。我个人认为,这些消息可以简单地只提到None,因为正如我们很快会看到的那样,None几乎没有模糊之处。所以如果你看到一些提到NoneType无法执行此操作或无法执行那个操作的TypeError消息,只需知道它只是使用了一个不能使用的None

另外,None是一个内置常量。只要你启动Python,就可以在模块、类或函数中使用它。相比之下,NoneType不是,你需要通过查询None来获取对它的引用。

>>> NoneType
NameError: name 'NoneType' is not defined
>>> type(None)
NoneType

你可以使用Python的身份函数id()检查None的唯一性。它返回分配给对象的唯一数字,每个对象都有一个。如果两个变量的ID相同,则实际上它们指向同一个对象。
>>> NoneType = type(None)
>>> id(None)
10748000
>>> my_none = NoneType()
>>> id(my_none)
10748000
>>> another_none = NoneType()
>>> id(another_none)
10748000
>>> def function_that_does_nothing(): pass
>>> return_value = function_that_does_nothing()
>>> id(return_value)
10748000

None 无法被覆盖

在较旧版本的Python(2.4之前),可以重新分配None,但现在不再可以。即使作为类属性或在函数范围内也不行。

# In Python 2.7
>>> class SomeClass(object):
...     def my_fnc(self):
...             self.None = 'foo'
SyntaxError: cannot assign to None
>>> def my_fnc():
        None = 'foo'
SyntaxError: cannot assign to None

# In Python 3.5
>>> class SomeClass:
...     def my_fnc(self):
...             self.None = 'foo'
SyntaxError: invalid syntax
>>> def my_fnc():
        None = 'foo'
SyntaxError: cannot assign to keyword

因此,可以安全地假设所有None引用都是相同的。没有任何“自定义”的None

使用is运算符测试None

在编写代码时,您可能会尝试像这样测试空值

if value==None:
    pass

或者像这样测试假设

if not value:
    pass

您需要理解其含义,通常显式说明是一个好主意。

情况一:测试值是否为None

为什么要这样做呢?

value is None

相比于
value==None

第一种等价于:

id(value)==id(None)

虽然表达式value==None实际上是这样应用的

value.__eq__(None)

如果值真的是None,那么你将得到你所期望的结果。
>>> nothing = function_that_does_nothing()
>>> nothing.__eq__(None)
True

在大多数情况下,结果是相同的,但__eq__()方法打开了一个门,使得准确性不能保证,因为它可以在类中被重写以提供特殊行为。
考虑这个类。
>>> class Empty(object):
...     def __eq__(self, other):
...         return not other

所以你尝试将其应用于None,结果运行正常。

>>> empty = Empty()
>>> empty==None
True

但是它也适用于空字符串。
>>> empty==''
True

尽管如此,
>>> ''==None
False
>>> empty is None
False

案例2:使用None作为布尔值

以下两个测试

if value:
    # Do something

if not value:
    # Do something

实际上被评估为
if bool(value):
    # Do something

if not bool(value):
    # Do something
None 是“假值”,意味着如果转换为布尔值,则返回 False,如果应用 not 运算符,则返回 True。但请注意,这不是 None 独有的属性。除了 False 本身之外,空列表、元组、集合、字典、字符串以及所有实现了 __bool__() 魔法方法并返回 False 的类的对象也具有该属性。
>>> bool(None)
False
>>> not None
True

>>> bool([])
False
>>> not []
True

>>> class MyFalsey(object):
...     def __bool__(self):
...         return False
>>> f = MyFalsey()
>>> bool(f)
False
>>> not f
True

因此,在以下方式测试变量时,请特别注意测试中包含或排除的内容:

def some_function(value=None):
    if not value:
        value = init_value()

在上文中,你是指当值被特定设置为None时调用init_value(),还是指将值设置为0、空字符串或空列表也应触发初始化?正如我所说,要小心。通常情况下,在Python中,明确比隐式更好。

None的实际应用

None作为信号值

None在Python中具有特殊的地位。它是一个常用的基准值,因为许多算法将其视为异常值。在这种情况下,它可以用作标志来表示某个条件需要一些特殊处理(例如设置默认值)。
您可以将None分配给函数的关键字参数,然后显式地测试它。
def my_function(value, param=None):
    if param is None:
        # Do something outrageous!

当尝试获取对象属性时,您可以将其作为默认值返回,并在执行特殊操作之前明确测试它。

value = getattr(some_obj, 'some_attribute', None)
if value is None:
    # do something spectacular!

默认情况下,字典的get()方法在尝试访问不存在的键时返回None

>>> some_dict = {}
>>> value = some_dict.get('foo')
>>> value is None
True

如果您尝试使用下标符号访问它,将会引发一个KeyError错误。

>>> value = some_dict['foo']
KeyError: 'foo'

同样地,如果您尝试弹出一个不存在的项目。
>>> value = some_dict.pop('foo')
KeyError: 'foo'

您可以使用默认值来抑制此情况,通常将默认值设置为None
value = some_dict.pop('foo', None)
if value is None:
    # Booom!

None既作为标志又作为有效值的使用

上述对None的使用方式适用于它被视为非有效值,而更像是执行特殊操作的信号。然而,在某些情况下,有时候需要知道None的来源,因为即使它被用作信号,它也可能是数据的一部分。

当使用getattr(some_obj, 'attribute_name', None)查询对象的属性时,如果返回None,则无法确定您尝试访问的属性是设置为None还是完全不存在于对象中。同样的情况也出现在访问字典的键时,例如some_dict.get('some_key'),您无法确定some_dict['some_key']是否缺失或仅设置为None。如果您需要该信息,则通常处理此问题的方法是在try/except结构内直接尝试访问属性或键:

try:
    # Equivalent to getattr() without specifying a default
    # value = getattr(some_obj, 'some_attribute')
    value = some_obj.some_attribute
    # Now you handle `None` the data here
    if value is None:
        # Do something here because the attribute was set to None
except AttributeError:
    # We're now handling the exceptional situation from here.
    # We could assign None as a default value if required.
    value = None
    # In addition, since we now know that some_obj doesn't have the
    # attribute 'some_attribute' we could do something about that.
    log_something(some_obj)

同样适用于字典:

try:
    value = some_dict['some_key']
    if value is None:
        # Do something here because 'some_key' is set to None
except KeyError:
    # Set a default
    value = None
    # And do something because 'some_key' was missing
    # from the dict.
    log_something(some_dict)

上面的两个例子展示了如何处理对象和字典的情况。那么函数呢?同样的方法,但我们使用双星号关键字参数来实现这一点:
def my_function(**kwargs):
    try:
        value = kwargs['some_key']
        if value is None:
            # Do something because 'some_key' is explicitly
            # set to None
    except KeyError:
        # We assign the default
        value = None
        # And since it's not coming from the caller.
        log_something('did not receive "some_key"')

None仅用作有效值

如果您发现代码中充斥着上述try/except模式,只是为了区分None标志和None数据,那么请使用另一个测试值。有一种模式,即将超出有效值集合的值插入到数据结构的数据中,并用于控制和测试特殊条件(例如边界、状态等)。这样的值称为sentinel,它可以像信号一样使用None。在Python中创建一个sentinel非常简单。

undefined = object()

上面的undefined对象是独特的,对于程序而言没有太多有趣的用途,因此它是作为标志位替代None的绝佳选择。但需要注意一些限制,关于这一点将在代码后面讨论。
使用函数:
def my_function(value, param1=undefined, param2=undefined):
    if param1 is undefined:
        # We know nothing was passed to it, not even None
        log_something('param1 was missing')
        param1 = None


    if param2 is undefined:
        # We got nothing here either
        log_something('param2 was missing')
        param2 = None

使用字典

value = some_dict.get('some_key', undefined)
if value is None:
    log_something("'some_key' was set to None")

if value is undefined:
    # We know that the dict didn't have 'some_key'
    log_something("'some_key' was not set at all")
    value = None

使用对象

value = getattr(obj, 'some_attribute', undefined)
if value is None:
    log_something("'obj.some_attribute' was set to None")
if value is undefined:
    # We know that there's no obj.some_attribute
    log_something("no 'some_attribute' set on obj")
    value = None

如我之前所提到的,自定义哨兵有一些注意事项。首先,它们不像None这样的关键字,因此Python不会保护它们。你可以在模块中的任何地方随时覆盖定义undefined,因此要小心如何公开和使用它们。其次,object()返回的实例不是单例。如果你调用它10次,你会得到10个不同的对象。最后,哨兵的使用非常独特。哨兵特定于它所使用的库,因此其范围通常应该限制在库的内部。它不应该“泄漏”出去。外部代码只有在其目的是扩展或补充库的API时才应该意识到它。


什么情况下是这样的:None == thing? - hkBst
@MichaelEkoka,你能分享这些令人印象深刻和有用的信息的来源吗? - Anidhya Bhatnagar
就我个人而言,我已经开始将 undefined = NotImplemented 进行设置。因为在任何地方都没有看到过这个类被使用,所以它像一个哨兵一样突出。通过比较 value is undefined,您可以获得完整的单例 is 行为。唯一的问题是它是真值,是特定于我的代码的。 - JL Peyret
@JLPeyret 关于使用NotImplemented有文档 https://docs.python.org/3/library/constants.html#NotImplemented。我认为将其用作哨兵不明智。一个好的哨兵必须是唯一的,并且在一个明确定义的范围内。`NotImplemented`类必须直接使用,因为它不能产生实例。但是,尽管该类本身是单例模式,但它是内置的,这使得它可以在任何意想不到的地方使用,并且可能比`None`更难以预测地出现在您的代码中。 - Michael Ekoka
@MichaelEkoka 对于作用域的看法很有趣,但我的经验恰好相反。以前使用了不同的哨兵方案(undefined = object(),像其他人一样),但是尝试从2个作用域进行比较 - 几个小时的错误修复,这正是内置避免的问题。我唯一遇到的问题是类评估为真值。然而,None 是一个非常糟糕的哨兵,我认为 - 比如说 DateofDeath,在许多情况下都会是 None - JL Peyret
显示剩余3条评论

74

它不像其他语言那样被称为null,而是称为None。 这个对象始终只有一个实例,因此如果你想检查等价性,可以使用x is None(身份比较),而不是x == None


29
我将寻找一种方法通过重新实例化None来打破Python。这样,所有那些与NoneType进行比较的聪明人都将获胜! - Zenexer

29
在Python中,要表示一个值的缺失,你可以使用None值(types.NoneType.None)代替对象和""(或len() == 0)代替字符串。因此:
if yourObject is None:  # if yourObject == None:
    ...

if yourString == "":  # if yourString.len() == 0:
    ...

关于“==”和“is”之间的区别,使用“==”测试对象身份应该足够了。但是,由于操作“is”被定义为对象身份操作,因此使用它可能更正确,而不是“==”。不确定是否有速度差异。

无论如何,您可以看一下:


8
据我所知,在所有编程语言中,(0 == false) 的返回值都是 true。这是因为 false 被编码为 0,而“true”则表示一切不为 0 的值。 然而,请注意,“is”运算符与“==”运算符并不相同。这更或多或少类似于 Java 中“==”和“equals”的区别。实际上,“is”运算符检查的是两个对象是否完全相同(即同一实例而非相同内容)! - Paolo Rovelli
15
Paolo,FYI,在每一种编程语言中,(0 == false)并不总是返回true。一个快速的反例是Scala,其中(0 == false)返回false,我相信Scala不是唯一将数字与布尔值进行比较时返回false的编程语言。 - Rob Wilton
4
在JavaScript中,0 == false返回true的原因是双等号运算符进行类型转换。但是,使用三等号运算符时,0 === false返回false,因为“number”和“boolean”是不同的类型。 - jkdev
1
@jkdev 是的,确实如此。这正是我想用Java中的“==”和“equals”的区别来指出的。你使用JavaScript中的“==”和“===”的例子实际上更好。 :) - Paolo Rovelli
1
不管怎样,我的错。我应该写成“大多数‘经典’编程语言”。确实,正如你所指出的那样,情况并非总是如此。特别是在更近期的编程语言(如Rust或Go)中,它们倾向于强制执行仅“相同类型”的比较。 - Paolo Rovelli
显示剩余2条评论

6
上面的答案只会对None返回True,但是还有一种叫做float('nan')的东西。你可以使用pandas的isnull
>>> import pandas as pd
>>> pd.isnull(None)
True
>>> pd.isnull(float('nan'))
True
>>> pd.isnull('abc')
False
>>> 

或者不使用 pandas

>>> a = float('nan')
>>> (a != a) or (a == None)
True
>>> a = None
>>> (a != a) or (a == None)
True
>>> 

这能够实现的原因是因为float('nan') != float('nan'):
>>> float('nan') == float('nan')
False
>>> float('nan') != float('nan')
True
>>> 

1
使用 f 字符串来解决这个问题。
year=None
year_val= 'null' if year is None else  str(year)
print(f'{year_val}')

null

1

Python中处理“空”元素的简单函数:

代码:

def is_empty(element) -> bool:
    """
    Function to check if input `element` is empty.

    Other than some special exclusions and inclusions,
    this function returns boolean result of Falsy check.
    """
    if (isinstance(element, int) or isinstance(element, float)) and element == 0:
        # Exclude 0 and 0.0 from the Falsy set.
        return False
    elif isinstance(element, str) and len(element.strip()) == 0:
        # Include string with one or more empty space(s) into Falsy set.
        return True
    elif isinstance(element, bool):
        # Exclude False from the Falsy set.
        return False
    else:
        # Falsy check.
        return False if element else True

测试:

print("Is empty?\n")
print('"" -> ', is_empty(""))
print('"      " -> ', is_empty("      "))
print('"A" -> ', is_empty("A"))
print('"a" -> ', is_empty("a"))
print('"0" -> ', is_empty("0"))
print("0 -> ", is_empty(0))
print("0.0 -> ", is_empty(0.0))
print("[] -> ", is_empty([]))
print("{} -> ", is_empty({}))
print("() -> ", is_empty(()))
print("[1, 2] -> ", is_empty([1, 2]))
print("(3, 5) -> ", is_empty((3, 5)))
print('{"a": 1} -> ', is_empty({"a": 1}))
print("None -> ", is_empty(None))
print("True -> ", is_empty(True))
print("False -> ", is_empty(False))
print("NaN -> ", is_empty(float("nan")))
print("range(0) -> ", is_empty(range(0)))

输出:

Is empty?

"" ->  True
"      " ->  True
"A" ->  False
"a" ->  False
"0" ->  False
0 ->  False
0.0 ->  False
[] ->  True
{} ->  True
() ->  True
[1, 2] ->  False
(3, 5) ->  False
{"a": 1} ->  False
None ->  True
True ->  False
False ->  False
NaN ->  False
range(0) ->  True

-1

根据真值测试,'None' 直接测试为 FALSE,因此最简单的表达式就足够了:

if not foo:

5
不会的。该表达式将对“任何”假值返回“True”,而不仅仅是“None”。 - insert_name_here
True,而简单的“if not foo:”不能正确回答原始问题,如所述。然而,Python是动态类型的,并且邀请减少语法协议,因此我认为考虑更简单、类似的结构是有效的。 - artejera

-1

Null是一种特殊的对象类型,类似于:

>>>type(None)
<class 'NoneType'>

您可以检查一个对象是否属于'NoneType'类:

>>>variable = None
>>>variable is None
True

更多信息可在Python文档中获取


但它实际上并不被称为“null”(?)。 - Peter Mortensen

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