Python 中的 Null 对象

Null object in Python

提问人:Lizard 提问时间:7/20/2010 最后编辑:Mateen UlhaqLizard 更新时间:8/23/2022 访问量:2277796

问:

如何在 Python 中引用 null 对象

Python 对象 null

评论


答:

1933赞 Ben James 7/20/2010 #1

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

要检查某些内容是否为 ,请使用 is 标识运算符:None

if foo is None:
    ...

评论

147赞 7/20/2010
选择的原因:后者可能会重载,并且在将有效对象与 None 进行比较时可能会中断(取决于它的实现方式,但你不希望每个人都考虑与 None 的比较,是吗?),而工作方式始终相同。egg is Noneegg == Noneis
108赞 Vidar 3/26/2013
为什么 Python 设计者不直接选择“null”。只是必须与众不同,不是吗!;)
41赞 Rushyo 6/12/2013
@Vidar 'None' 不是缺少对象(因为 'null' 是 null 引用),它是一个实际的对象。你永远不会得到一个“None”对象,该对象属于“NoneType”以外的任何类型。它也不是一个语言概念。它不是内置于 Python 语言中的,它是 Python 标准库的一部分。None !== (咳咳) Null
13赞 Rushyo 9/2/2014
@naught101 它不会有任何用处;因为没有指针的概念。如果您使用的是 ctypes 库,则 None 将用作地址 0 的同义词,但仍然没有“null 引用”的语言概念。在 Python 中,你总是引用某种对象,即使该对象是 None。我怀疑这大大简化了语言。
7赞 Michahell 4/9/2015
很棒的演讲,解释了“十亿美元错误”,即空引用:infoq.com/presentations/....除 null 之外的其他选项是 Options 或 Maybe (自定义) 类型。
74赞 AndiDog 7/20/2010 #2

它不像其他语言那样被称为 null,而是称为 None。此对象始终只有一个实例,因此如果需要,可以检查是否等同于 (标识比较) 而不是 。x is Nonex == None

评论

29赞 Zenexer 8/27/2013
我将找到一种方法通过重新实例化来破坏 Python。然后,所有那些与谁比较的聪明人都会胜利!NoneNoneType
29赞 Paolo Rovelli 3/3/2013 #3

在 Python 中,要表示缺少值,可以使用 None 值 (types.NoneType.None) 表示对象,“ (或 len() == 0) 表示字符串。因此:

if yourObject is None:  # if yourObject == None:
    ...

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

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

无论如何,您可以看看:

评论

8赞 Paolo Rovelli 12/13/2013
据我所知,(0 == false)在每种编程语言中都返回true。这是因为 false 被编码为 0,而“true”被编码为所有不是 0 的东西。但是,请不要说“is”运算符与“==”运算符不同。这或多或少与 Java 中“==”和“equals”之间的区别相同。事实上,运算符“is”检查两个对象是否相同(相同的实例和不相同的内容)!
15赞 Rob Wilton 7/1/2014
Paolo,仅供参考,(0 == false) 在每种编程语言中都不返回 true。一个快速计数器的例子是 Scala,其中 (0 == false) 返回 false,我确信 Scala 不是唯一在将数字与布尔值进行比较时返回 false 的编程语言。
4赞 jkdev 5/19/2016
在 JavaScript 中,返回的原因是因为双等于运算符确实存在类型强制。但是,使用三等运算符会返回 ,因为 'number' 和 'boolean' 是不同的类型。0 == falsetrue0 === falsefalse
1赞 scooter-dangle 8/15/2017
@PaoloRovelli,在 Lua、Ruby 和 Clojure 中,0 被视为条件语句。在 Rust 和 Go 中,是无法比较平等的不同类型。true0false
1赞 Paolo Rovelli 5/19/2021
@jkdev 是的,确实如此。这正是我想用 Java“==”和“equals”区别指出的。您使用 JavaScript “==” 和 “===” 的示例实际上是一个更好的示例。:)
-1赞 artejera 9/25/2017 #4

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

if not foo:

评论

5赞 insert_name_here 9/25/2017
不,它不会。该表达式将返回任何虚假值,而不仅仅是 .TrueNone
0赞 artejera 10/6/2017
没错,如上所述,简单的“if not foo:”不会正确回答原始问题。然而,python 是动态类型的,并且邀请减少语法协议,所以在我看来,考虑更简单、类似的结构似乎是有效的。
253赞 Michael Ekoka 1/29/2018 #5

None,Python 的 null?

Python 中没有;取而代之的是.如前所述,测试某些内容是否被指定为值的最准确方法是使用标识运算符,该运算符测试两个变量是否引用同一对象。nullNoneNoneis

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

基础知识

有且只能有一个None

None是该类的唯一实例,任何进一步实例化该类的尝试都将返回相同的对象,从而生成单例。Python 的新手经常看到错误消息,其中提到并想知道它是什么。我个人认为,这些信息可以简单地提到名字,因为正如我们很快就会看到的那样,几乎没有歧义的余地。因此,如果您看到一些消息提到不能这样做或不能这样做,请知道它只是以它不能的方式使用。NoneTypeNoneNoneTypeNoneNoneTypeErrorNoneTypeNone

此外,是一个内置常量。一旦你启动了 Python,它就可以从任何地方使用,无论是在模块、类还是函数中。 相比之下,不是,您需要首先通过查询其类来获取对它的引用。NoneNoneTypeNone

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

你可以用Python的标识函数来检查的唯一性。它返回分配给对象的唯一编号,每个对象都有一个。如果两个变量的 id 相同,则它们实际上指向同一个对象。Noneid()

>>> 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

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

测试使用运算符Noneis

在编写代码时,你可能会想像这样测试 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是一个“falsey”,这意味着如果转换为布尔值,它将返回,如果应用运算符,它将返回。但请注意,它不是 独有的属性。除了它本身之外,该属性还由空列表、元组、集合、字典、字符串以及 0 以及实现 magic 方法的类中的所有对象共享 return .FalsenotTrueNoneFalse__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()

在上面,您的意思是在值专门设置为 时调用,还是意味着设置为 的值、空字符串或空列表也应该触发初始化?就像我说的,要注意。通常情况下,在 Python 中,显式比隐式更好init_value()None0

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用作标志和有效值

上面描述的 apply 的用法不被认为是一个有效的值,而更像是一个信号,要做一些特别的事情。然而,在某些情况下,知道来自哪里有时很重要,因为即使它被用作信号,它也可能是数据的一部分。NoneNone

当您查询对象的属性时,返回不会告诉您尝试访问的属性是否已设置为,或者该属性是否完全不在对象中。从字典中访问密钥时的情况相同,例如 ,您不知道是否丢失或是否只是设置为 。如果您需要该信息,处理此问题的常用方法是直接尝试从构造中访问属性或键:getattr(some_obj, 'attribute_name', None)NoneNonesome_dict.get('some_key')some_dict['some_key']Nonetry/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)

与 dict 类似:

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仅用作有效值

如果你发现你的代码中充斥着上述模式,只是为了区分标志和数据,那么只需使用另一个测试值。有一种模式,即在有效值集之外的值作为数据的一部分插入到数据结构中,并用于控制和测试特殊条件(例如边界、状态等)。这样的值称为哨兵,它可以用作信号的方式。在 Python 中创建哨兵是微不足道的。try/exceptNoneNoneNone

undefined = object()

上面的对象是独一无二的,不会做任何程序可能感兴趣的事情,因此它是作为标志的绝佳替代品。一些注意事项适用,在代码之后有更多内容。undefinedNone

带功能

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

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

评论

0赞 hkBst 11/1/2018
怎么样:没有 == 东西?
0赞 Michael Ekoka 11/2/2018
@hkBst 我猜你的问题是关于 python 如何评估平等。看看这个 stackoverflow.com/questions/3588776/...
0赞 hkBst 11/2/2018
啊,所以通常最终还是会打电话给thing.__eq__的 IIUC?在这种情况下,我撤回了我的建议。
0赞 Anidhya Bhatnagar 12/25/2018
您@MichaelEkoka分享这些令人印象深刻且有用的信息的来源。
0赞 Michael Ekoka 4/14/2019
标准做法。你可以通过阅读 Python 教程找到一些提示,但阅读流行项目的源代码是我快速沉浸在惯用 Python 中的第一建议。
-1赞 user13235427 4/11/2020 #6

Null 是一种特殊的对象类型,如下所示:

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

您可以检查对象是否在类“NoneType”中:

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

有关详细信息,请参阅 Python 文档

评论

0赞 Peter Mortensen 6/20/2020
但它实际上并不被称为“null”(?)。
1赞 s_mj 1/16/2021 #7

使用字符串来解决此问题。f

year=None
year_val= 'null' if year is None else  str(year)
print(f'{year_val}')

null
6赞 U13-Forward 9/12/2021 #8

上面的答案只会得到 ,但有这样的事情。你可以使用 pandas :TrueNonefloat('nan')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赞 Dheemanth Bhat 7/6/2022 #9

在 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