Python中的空对象

新手上路,请多包涵

如何在 Python 中引用 null 对象

原文由 Lizard 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 572
2 个回答

在 Python 中,’null’ 对象是单例 None

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

 if foo is None:
    ...

原文由 Ben James 发布,翻译遵循 CC BY-SA 4.0 许可协议

None ,Python 的 null?

Python 中没有 null ;取而代之的是 None 。如前所述,测试某物是否已被赋予 None 作为值的最准确方法是使用 is 身份运算符,它测试两个变量是否引用同一对象。

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

基础

存在且只能是一个 None

None 是该类的唯一实例 NoneType 任何进一步实例化该类的尝试都将返回相同的对象,这使得 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 运算符

在编写代码时,您可能会想像这样测试 Noneness

 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 is a “falsey”, meaning that if cast to a boolean it will return False and if applied the not operator it will return 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()

在上面,当值专门设置为 --- 时,您的意思是调用 init_value() None ,还是您的意思是将值设置为 0 ,或者空字符串或空列表也应该触发初始化?就像我说的,要留心。通常情况下,在 Python 中 explicit 优于 implicit

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,则外部代码应该只知道它。

原文由 Michael Ekoka 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题