如何在 Python 中引用 null 对象?
没有,Python 的空值?
Python 中没有 null
;取而代之的是 None
。如前所述,测试是否已将 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
没有一个不能被覆盖
在 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
在 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
的来源很重要,因为即使它用作信号,它也可能是数据的一部分。
当您使用 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 时才应该意识到这一点。
它不像其他语言那样称为 null,而是 None
。此对象始终只有一个实例,因此您可以根据需要使用 x is None
(身份比较)而不是 x == None
检查等价性。
None
找到一种破坏 Python 的方法。那么所有与NoneType
比较的聪明人都会胜利!
在 Python 中,为了表示值的缺失,您可以对对象使用 None 值 (types.NoneType.None),对字符串使用 ""(或 len() == 0)。所以:
if yourObject is None: # if yourObject == None:
...
if yourString == "": # if yourString.len() == 0:
...
关于“==”和“is”之间的区别,使用“==”测试对象身份就足够了。但是,由于操作“is”被定义为对象标识操作,使用它可能更正确,而不是“==”。不知道是否有速度差异。
无论如何,你可以看看:
Python 内置常量文档页面。
Python 真值测试文档页面。
0 == false
返回 true
的原因是双等号运算符确实进行了强制类型转换。但是,对于三等号运算符,0 === false
返回 false
,因为“数字”和“布尔”是不同的类型。
true
。在 Rust 和 Go 中,0
和 false
是不能比较相等的不同类型。
上面的答案只会为 None
产生 True
,但有 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
>>>
使用 f
字符串来解决这个问题。
year=None
year_val= 'null' if year is None else str(year)
print(f'{year_val}')
null
在 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(""))
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)))
输出:
"" -> 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
根据 Truth value testing,'None' 直接测试为 FALSE,因此最简单的表达式就足够了:
if not foo:
True
,而不仅仅是 None
。
Null 是一种特殊的对象类型,例如:
>>>type(None)
<class 'NoneType'>
您可以检查对象是否在类“NoneType”中:
>>>variable = None
>>>variable is None
True
如需更多信息,请访问 Python Docs
egg is None
而不是egg == None
的原因:后者可能会被重载,并且在将有效对象与 None 进行比较时可能会中断(取决于它的实现方式,但您不要期望每个人都将与 None 进行比较帐户,是吗?),而is
总是一样的。