【问题标题】:Referring to the null object in Python在 Python 中引用 null 对象
【发布时间】:2011-03-18 09:42:53
【问题描述】:

如何在 Python 中引用 null 对象?

【问题讨论】:

    标签: python null


    【解决方案1】:

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

    检查“Noneness”的最佳方法是使用身份运算符is

    if foo is None:
        ...
    

    【讨论】:

    • 以及选择egg is None而不是egg == None的原因:后者可能会被重载,并且在将valid object与None进行比较时可能会中断(取决于它的实现方式,但你不会希望每个人都考虑与 None 的比较,是吗?),而 is 总是一样的。
    • python 设计者为什么不选择“null”。只是必须不同,不是吗! ;)
    • @Vidar 'None' 不是缺少一个对象(因为 'null' 是一个空引用),它是一个实际的对象。你永远不会得到一个'None'对象传递一个'NoneType'以外的任何类型的对象。它也不是语言概念。它不是 Python 语言内置的,它是 Python 标准库的一部分。无!==(咳咳)空
    • @naught101 这将毫无用处;因为没有指针的概念。如果您使用的是 ctypes 库,则 None 将用作地址零的同义词,但仍然没有“空引用”的语言概念。在 Python 中,您总是会引用某种对象,即使该对象是 None。我怀疑这大大简化了语言。
    • 很好的演示文稿解释了“十亿美元的错误”,即空参考:infoq.com/presentations/…。其他选项而不是 null 是选项或可能(自定义)类型。
    【解决方案2】:

    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 是一个“假”,这意味着如果转换为布尔值,它将返回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)
    

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

    如果您发现您的代码中散布着上述try/except 模式只是为了区分None 标志和None 数据,那么只需使用另一个测试值。有一种模式,其中超出有效值集的值作为数据结构中数据的一部分插入,用于控制和测试特殊条件(例如边界、状态等)。这样的值称为sentinel,它可以像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 时才应该意识到这一点。

    【讨论】:

    • 怎么样:无 == 事情?
    • @hkBst 我猜你的问题是关于 python 如何评估相等性。看看这个stackoverflow.com/questions/3588776/…
    • 啊,所以通常还是会调用 thing.__eq__ 的 IIUC?在这种情况下,我撤回我的建议。
    • @MichaelEkoka 你能分享这个令人印象深刻和有用的信息的来源吗?
    • 标准做法。您可以通过Python tutorial 找到一些提示,但阅读热门项目的源代码是我快速沉浸在idiomatic Python 中的第一条建议。
    【解决方案3】:

    它不像其他语言那样称为null,而是None。此对象始终只有一个实例,因此您可以根据需要检查与x is None(身份比较)而不是x == None 的等价性。

    【讨论】:

    • 我将通过重新实例化 None 找到一种方法来破坏 Python。那么所有与NoneType比较的聪明人都会胜利!
    【解决方案4】:

    在 Python 中,为了表示值的缺失,可以对对象使用 None 值 (types.NoneType.None) 和 ""(或 len() == 0)用于字符串。因此:

    if yourObject is None:  # if yourObject == None:
        ...
    
    if yourString == "":  # if yourString.len() == 0:
        ...
    

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

    不管怎样,你可以看看:

    【讨论】:

    • 据我所知,(0 == false) 在每种编程语言中都返回 true。这是因为 false 被编码为 0,而“true”被编码为非 0 的所有内容。但是,请注意“is”运算符与“==”运算符不同。这或多或少与 Java 中“==”和“equals”之间的区别相同。实际上,运算符“is”检查两个对象是否相同(相同的实例和不同的内容)!
    • Paolo,仅供参考,(0 == false) 并非在每种编程语言中都返回 true。一个快速反例是 Scala,其中 (0 == false) 返回 false,我确信 Scala 不是唯一在将数字与布尔值进行比较时返回 false 的编程语言。
    • 在 JavaScript 中,0 == false 返回 true 的原因是因为双等于运算符确实进行了类型强制。但是,对于三等号运算符,0 === false 返回 false,因为“数字”和“布尔”是不同的类型。
    • @PaoloRovelli,在 Lua、Ruby 和 Clojure 中,0 在条件句中被视为 true。在 Rust 和 Go 中,0false 是无法比较相等的不同类型。
    • @jkdev 是的,确实如此。这正是我想用Java“==”和“等于”的区别来指出的。您使用 JavaScript "==" 和 "===" 的示例实际上是一个更好的示例。 :)
    【解决方案5】:

    上面的答案只会导致TrueNone,但是有float('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
    >>> 
    

    【讨论】:

      【解决方案6】:

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

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

      【讨论】:

        【解决方案7】:

        Null 是一种特殊的对象类型,例如:

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

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

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

        更多信息请访问Python Docs

        【讨论】:

        • 但实际上并不叫“null”(?)。
        【解决方案8】:

        根据Truth value testing,'None' 直接测试为 FALSE,所以最简单的表达式就足够了:

        if not foo:
        

        【讨论】:

        • 不,不会。对于 any 虚假值,该表达式将返回 True,而不仅仅是 None
        • 是的,如前所述,简单的“if not foo:”不会正确回答原始问题。但是,python 是动态类型的,并且需要减少语法协议,所以对我来说,考虑更简单、相似的结构似乎是有效的。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-06-16
        • 2011-06-05
        • 2015-03-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多