17 KiB
Python 中的 Null:理解 Python 的 NoneType 对象
*立即观看**本教程有真实 Python 团队创建的相关视频课程。和文字教程一起看,加深理解: Python 的 None: Null in Python
如果你有使用其他编程语言的经验,如 C 或 Java ,那么你可能听说过 null 的概念。许多语言用这个来表示不指向任何东西的指针,表示变量何时为空,或者标记尚未提供的默认参数。在那些语言中,null通常被定义为0,但是在 Python 中的null是不同的。
Python 使用关键字 None来定义null对象和变量。虽然在其他语言中,None确实服务于与null相同的一些目的,但它完全是另一种野兽。与 Python 中的null一样,None没有被定义为0或其他任何值。在 Python 中,None是对象,是一等公民!
在本教程中,您将学习:
- 什么是
None以及如何测试 - 何时以及为何使用
None作为默认参数 - 在你的回溯中
None和NoneType是什么意思 - 如何在型式检验中使用
None - Python 中的
null是如何工作的
免费奖励: 并学习 Python 3 的基础知识,如使用数据类型、字典、列表和 Python 函数。
理解 Python 中的空值
None是函数中没有return语句时函数返回的值:
>>> def has_no_return():
... pass
>>> has_no_return()
>>> print(has_no_return())
None
当你调用has_no_return()时,你看不到任何输出。然而,当您打印对它的调用时,您将看到它返回的隐藏的None。
事实上,None如此频繁地作为返回值出现,以至于 Python REPL 不会打印None,除非你明确地告诉它:
>>> None
>>> print(None)
None
None本身没有输出,但是打印它会将None显示到控制台。
有趣的是, print() 本身没有返回值。如果你试图打印一个对 print() 的调用,那么你会得到None:
>>> print(print("Hello, World!"))
Hello, World!
None
这看起来可能很奇怪,但是print(print("..."))向你展示了内在print()返回的None。
None也常用作缺失或默认参数的信号。例如,None在 list.sort 的文档中出现两次:
>>> help(list.sort)
Help on method_descriptor:
sort(...)
L.sort(key=None, reverse=False) -> None -- stable sort *IN PLACE*
这里,None是key参数的默认值,也是返回值的类型提示。help的确切产量可能因平台而异。当您在您的解释器中运行这个命令时,您可能会得到不同的输出,但是它将是相似的。
使用 Python 的空对象None
通常,你会使用None作为比较的一部分。一个例子是当你需要检查某个结果或参数是否为None时。从 re.match 中取你得到的结果。你的正则表达式匹配给定的字符串了吗?您将看到两种结果之一:
- **返回一个
Match对象:**你的正则表达式找到一个匹配。 - **返回一个
None对象:**你的正则表达式没有找到匹配。
在下面的代码块中,您正在测试模式"Goodbye"是否匹配一个字符串:
>>> import re
>>> match = re.match(r"Goodbye", "Hello, World!")
>>> if match is None:
... print("It doesn't match.")
It doesn't match.
这里,您使用is None来测试模式是否匹配字符串"Hello, World!"。这个代码块演示了一个重要的规则,当您检查None时要记住:
- 使用了身份运算符
is和is not吗? - 不要使用等式运算符
==和!=。
当您比较用户定义的对象时,等式操作符可能会被愚弄,这些对象被覆盖:
>>> class BrokenComparison:
... def __eq__(self, other):
... return True
>>> b = BrokenComparison()
>>> b == None # Equality operator
True
>>> b is None # Identity operator
False
这里,等式运算符==返回错误的答案。另一方面,身份操作符is 不会被愚弄,因为你不能覆盖它。
**注意:**关于如何与None进行比较的更多信息,请查看该做的和不该做的:Python 编程建议。
None是福尔西,意思是not None是True。如果您只想知道结果是否为假,那么如下测试就足够了:
>>> some_result = None
>>> if some_result:
... print("Got a result!")
... else:
... print("No result.")
...
No result.
输出没有告诉你some_result就是None,只告诉你它是假的。如果你必须知道你是否有一个None对象,那么使用is和is not。
以下对象也是假的:
关于比较、真值和假值的更多信息,你可以阅读如何使用 Python or操作符,如何使用 Python and操作符,以及如何使用 Python not操作符。
在 Python 中声明空变量
在一些语言中,变量来源于声明。它们不需要被赋予初始值。在这些语言中,某些类型变量的初始默认值可能是null。然而,在 Python 中,变量来源于赋值语句。看一下下面的代码块:
>>> print(bar)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'bar' is not defined
>>> bar = None
>>> print(bar)
None
这里,你可以看到一个值为None的变量不同于一个未定义的变量。Python 中的所有变量都是通过赋值产生的。如果你给一个变量赋值None,它在 Python 中只会以null开始。
使用None作为默认参数
通常,您会使用None作为可选参数的默认值。这里有一个很好的理由使用None而不是可变类型,比如 list。想象一个这样的函数:
def bad_function(new_elem, starter_list=[]):
starter_list.append(new_elem)
return starter_list
bad_function()包含令人讨厌的惊喜。当您使用现有列表调用它时,它工作得很好:
>>> my_list = ['a', 'b', 'c']
>>> bad_function('d', my_list)
['a', 'b', 'c', 'd']
在这里,您将'd'添加到列表的末尾,没有任何问题。
但是,如果您多次调用这个函数而没有使用starter_list参数,那么您将开始看到不正确的行为:
>>> bad_function('a')
['a']
>>> bad_function('b')
['a', 'b']
>>> bad_function('c')
['a', 'b', 'c']
在定义函数时,starter_list的默认值只计算一次,所以每次没有传递现有列表时,代码都会重用它。
构建这个函数的正确方法是使用None作为默认值,然后测试它并根据需要实例化一个新的列表:
1>>> def good_function(new_elem, starter_list=None):
2... if starter_list is None: 3... starter_list = [] 4... starter_list.append(new_elem)
5... return starter_list
6...
7>>> good_function('e', my_list)
8['a', 'b', 'c', 'd', 'e']
9>>> good_function('a')
10['a']
11>>> good_function('b')
12['b']
13>>> good_function('c')
14['c']
good_function()通过每次调用创建一个新的列表,而不是传递一个现有的列表,按照您想要的方式进行操作。它之所以有效,是因为您的代码每次调用带有默认参数的函数时都会执行第 2 行和第 3 行。
在 Python 中使用None作为空值
当None是有效的输入对象时,你会怎么做?例如,如果good_function()可以向列表中添加元素,也可以不添加,而None是要添加的有效元素,那会怎么样呢?在这种情况下,您可以定义一个专门用作默认的类,同时与None相区别:
>>> class DontAppend: pass
...
>>> def good_function(new_elem=DontAppend, starter_list=None):
... if starter_list is None:
... starter_list = []
... if new_elem is not DontAppend:
... starter_list.append(new_elem)
... return starter_list
...
>>> good_function(starter_list=my_list)
['a', 'b', 'c', 'd', 'e']
>>> good_function(None, my_list)
['a', 'b', 'c', 'd', 'e', None]
在这里,类DontAppend作为不追加的信号,所以你不需要None来做这个。这让你可以在需要的时候添加None。
当None也可能是返回值时,您可以使用这种技术。例如,如果在字典中找不到关键字,默认情况下, dict.get 会返回None。如果None在您的字典中是一个有效值,那么您可以这样调用dict.get:
>>> class KeyNotFound: pass
...
>>> my_dict = {'a':3, 'b':None}
>>> for key in ['a', 'b', 'c']:
... value = my_dict.get(key, KeyNotFound)
... if value is not KeyNotFound:
... print(f"{key}->{value}")
...
a->3
b->None
这里您已经定义了一个定制类KeyNotFound。现在,当一个键不在字典中时,你可以返回KeyNotFound,而不是返回None。这使得您可以返回None,而这是字典中的实际值。
回溯中的解密None
当NoneType出现在你的回溯中,说明你没想到会是None的东西实际上是None,你试图用一种你不能用None的方式使用它。几乎总是,这是因为你试图在它上面调用一个方法。
例如,您在上面的my_list中多次调用了 append() ,但是如果my_list不知何故变成了列表之外的任何东西,那么append()就会失败:
>>> my_list.append('f')
>>> my_list
['a', 'b', 'c', 'd', 'e', None, 'f']
>>> my_list = None
>>> my_list.append('g')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'append'
这里,您的代码引发了非常常见的AttributeError,因为底层对象my_list不再是一个列表。您已经将它设置为None,它不知道如何append(),因此代码抛出一个异常。
当您在代码中看到类似这样的回溯时,首先查找引发错误的属性。在这里,是append()。从那里,您将看到您试图调用它的对象。在这种情况下,它是my_list,您可以从回溯上方的代码中看出这一点。最后,弄清楚这个对象是如何变成None的,并采取必要的步骤来修改代码。
检查 Python 中的空值
在 Python 中,有两种类型检查情况需要关注null。第一种情况是你在返回NoneT5 的时候:
>>> def returns_None() -> None:
... pass
这种情况类似于根本没有return语句时,默认情况下返回None。
第二种情况更具挑战性。它是您获取或返回一个值的地方,这个值可能是None,但也可能是其他(单个)类型。这种情况就像你对上面的re.match所做的,它返回一个Match对象或者None。
对于参数,过程是相似的:
from typing import Any, List, Optional
def good_function(new_elem:Any, starter_list:Optional[List]=None) -> List:
pass
从上面修改good_function(),从typing导入Optional,返回一个Optional[Match]。
在引擎盖下看一看
在许多其他语言中,null只是0的同义词,但 Python 中的null是一个成熟的对象:
>>> type(None)
<class 'NoneType'>
这一行显示None是一个对象,它的类型是NoneType。
None本身作为 Python 中的null内置于语言中:
>>> dir(__builtins__)
['ArithmeticError', ..., 'None', ..., 'zip']
在这里,你可以看到__builtins__列表中的None,它是解释器为 builtins 模块保留的字典。
None是一个关键词,就像True和False一样。但是正因为如此,你不能像你可以直接从__builtins__到达None,比如说ArithmeticError。不过,你可以用一个 getattr() 的招数得到它:
>>> __builtins__.ArithmeticError
<class 'ArithmeticError'>
>>> __builtins__.None
File "<stdin>", line 1
__builtins__.None
^
SyntaxError: invalid syntax
>>> print(getattr(__builtins__, 'None'))
None
当你使用getattr()时,你可以从__builtins__中获取实际的None,这是你简单地用__builtins__.None索取无法做到的。
尽管 Python 在许多错误消息中输出了单词NoneType,但是NoneType在 Python 中并不是一个标识符。它不在builtins。只有用type(None)才能到达。
None是的独生子。也就是说,NoneType类只给你同一个None实例。您的 Python 程序中只有一个None:
>>> my_None = type(None)() # Create a new instance
>>> print(my_None)
None
>>> my_None is None
True
即使您试图创建一个新实例,您仍然会得到现有的None。
你可以用 id() 证明None和my_None是同一个对象:
>>> id(None)
4465912088
>>> id(my_None)
4465912088
这里,id为None和my_None输出相同的整数值意味着它们实际上是同一个对象。
**注意:**由id产生的实际值会因系统而异,甚至会因程序执行而异。在最流行的 Python 运行时 CPython 下,id()通过报告对象的内存地址来完成工作。住在同一个内存地址的两个对象是同一个对象。
如果你试图赋值给None,那么你会得到一个 SyntaxError :
>>> None = 5
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
SyntaxError: can't assign to keyword
>>> None.age = 5
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'age'
>>> setattr(None, 'age', 5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'age'
>>> setattr(type(None), 'age', 5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't set attributes of built-in/extension type 'NoneType'
上面所有的例子都表明你不能修改None或者NoneType。它们是真正的常数。
您也不能子类化NoneType:
>>> class MyNoneType(type(None)):
... pass
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: type 'NoneType' is not an acceptable base type
这个回溯表明解释器不会让你创建一个继承自type(None)的新类。
结论
None是 Python 工具箱中一个强大的工具。与True和False一样,None是一个不可变的关键字。作为 Python 中的null,您可以用它来标记缺失的值和结果,甚至是默认参数,这是比可变类型更好的选择。
现在你可以:
- 用
is和is not测试None - 选择
None何时是代码中的有效值 - 使用
None及其替代参数作为默认参数 - 破译回溯中的
None和NoneType - 在类型提示中使用
None和Optional
Python 中的null怎么用?在下面的评论区留下你的评论吧!
立即观看本教程有真实 Python 团队创建的相关视频课程。和文字教程一起看,加深理解: Python 的 None: Null in Python***