Python中的空对象

1476 投票
10 回答
2326712 浏览
提问于 2025-04-16 01:34

在Python中,我该如何提到“空对象”呢?

10 个回答

74

在其他编程语言中,我们常常称之为“null”,但在这里它叫做None。这个对象只有一个实例,所以如果你想检查某个变量是不是这个对象,可以用 x is None 来比较(这是一种身份比较),而不是用 x == None

255

None,Python的空值?

在Python中没有null这个概念,取而代之的是None。最准确的方式来检查某个值是否是None,就是使用is这个身份运算符,它可以判断两个变量是否指向同一个对象。

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

基础知识

只有一个None

NoneNoneType这个类的唯一实例,任何尝试创建这个类的新实例都会返回同一个对象,这使得None成为一个单例。在学习Python的新手常常会看到提到NoneType的错误信息,心里会想这是什么。个人认为这些信息可以直接提到None,因为如我们稍后会看到的,None几乎没有歧义。所以如果你看到某个TypeError的消息提到NoneType不能做某事,只需知道这是在用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

检查None时使用is运算符

在写代码时,你可能会想用以下方式来检查是否是None

if value==None:
    pass

或者用这种方式检查真假

if not value:
    pass

你需要理解这样做的含义,以及为什么通常明确一点是个好主意。

情况1:检查一个值是否是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本身,这个特性还适用于空列表、元组、集合、字典、字符串,以及0,还有所有实现了__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的数据,那么可以使用另一个测试值。有一种模式是将一个不在有效值集合中的值插入到数据结构中,用于控制和测试特殊条件(例如边界、状态等)。这样的值被称为哨兵,可以像None一样用作信号。在Python中创建一个哨兵是很简单的。

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时,才应了解它。

1944

在Python中,'null'对象就是那个唯一的None

要检查某个东西是不是None,可以用is这个身份运算符:

if foo is None:
    ...

撰写回答