如何在Python中引用空对象?


当前回答

None, Python为空?

Python中没有null;相反,没有。如前所述,测试某物是否赋值为None的最准确方法是使用is标识操作符,它测试两个变量是否指向同一个对象。

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

最基本的

有且只能有一个无

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,请使用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

所以你试了一下,它没有,它工作

>>> 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”,这意味着如果转换为布尔类型,它将返回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在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用法就适用了。然而,在某些情况下,知道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"')

无,仅用作有效值

如果您发现代码中到处都是上面的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

与dict类型

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不会保护它们。你可以在模块定义的任何时候、任何地方覆盖上面的未定义,所以要注意如何公开和使用它们。接下来,object()返回的实例不是单例。如果调用10次,就会得到10个不同的对象。最后,哨兵的使用是非常特殊的。一个哨兵是特定于它所使用的库的,因此它的范围通常应该限制在库的内部。它不应该“泄露”出去。只有当外部代码的目的是扩展或补充库的API时,它们才应该知道它。

其他回答

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

if not foo:

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

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

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

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

总之,你可以看看:

Python内置常量文档页。 Python真值测试文档页。

Null是一个特殊的对象类型,比如:

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

你可以检查一个对象是否在类'NoneType'中:

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

更多信息可以在Python文档中找到

在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

上面的答案只会为None的结果为True,但存在float('nan')这样的东西。你可以使用pandas isnull:

>>> import pandas as pd
>>> pd.isnull(None)
True
>>> pd.isnull(float('nan'))
True
>>> pd.isnull('abc')
False
>>> 

或者没有熊猫:

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