理解Python中的NoneType对象
2020-06-16
编译:老齐
与本文相关的图书推荐:《跟老齐学Python:轻松入门》
本书适合零基础的学习者,书中详细而全面地讲述了Python的基本对象类型、语句、函数、对象和类、标准库和常用模块等知识。本书还特别强调学习编程语言的方法,强调“授人以鱼”的同时“授人以渔”。
在C、Java等类型的语言中,都有null
,它常常被定义为与0
等效。但是,在Python中并非如此。Python中用关键词None
表征null
对象,它并不是0
,它是Python中的第一类对象。
None是什么
对于函数,如果在函数体中没有return
语句,会默认返回None
。
1 | def has_no_return(): |
如果调用has_no_return()
函数,会看到没有任何返回结果,然而,如果用print()
函数打印返回结果,会把默认返回的None
打印出来。
通常情况None
只有在执行了print()
之后才会显示。
1 | None |
None
本身没有返回结果,很有意思的是,print()
自身也没有返回值,如果你打印print()
的返回结果,会得到None
。
1 | "Hello, World!")) print(print( |
感觉有点奇怪吧,print(print("..."))
所显示的None
就是里面的print()
返回值。
None
还常常作为缺失或者默认参数值,例如:
1 | help(list.sort) |
在这里,None
是参数key
的值,也是返回值类型提示。
使用None
通常,None
作为返回值或者某些参数的值,比如在正则表达式中,如果没有匹配对象,则返回None
。
1 | import re |
第2行要匹配字符串Hello, World
,如果不能匹配则返回None
,通过这段代码,我们要知道:
- 判断是否是
None
对象,应该使用is
或is not
, - 而不是使用
==
或者!=
。
再比如,我们自定义一个对象,分别用两种方式进行判断:
1 | class BrokenComparison: |
显然,第5行,使用==
,返回的结果是错误的。只有用is
这个对象身份判断的运算符,才能得到正确结果。
None
是假,也就意味着not None
是真了。
1 | None some_result = |
在第2行条件判断中,并没有显式地写some_result is None
,这是因为None
本身就是假,与之类似,下面的这些对象也是假:
- 空列表
- 空字典
- 空元组
- 空字符串
- 0
- False
在Python中,变量必须与对象关联,None
是一类Python对象,所以也可以被变量引用。
1 | print(bar) |
在有的语言中,如果定义了变量,但是没有赋值,该变量值会是null
。但Python中的变量不能单独存在,如果将变量与None
建立引用关系,并非意味着该变量是空。
None作为参数默认值
更多情况下,你看到的可能是以None
为默认参数值。比如:
1 | def bad_function(new_elem, starter_list=[]): |
这个例子中,函数的参数starter_list
的默认值是一个空列表,这种方式不值得提倡,因为它会隐藏着一些BUG。
如果我们先创建了一个列表,并且通过上面的函数,向该类表中追加一个元素,如下:
1 | 'a', 'b', 'c'] my_list = [ |
这没有什么问题,实现了前面的预想。但是,如果不给starter_list
提供参数,即使用默认参数,会怎么样?
1 | 'a') bad_function( |
第1行调用的时候,返回了一个列表,列表中只有一个元素。第3行再次调用的时候,同样也没有给starter_list
提供值,它依然应该是原有列表——注意观察定义函数时的参数。但是,返回值是在上一次调用返回结果中增加了新元素。这就是问题所在,每次重新调用此函数,参数starter_list=[]
居然不起作用了。
所以,正如函数名字显示的,这样定义的函数不是一个“好”函数。
推荐的定义方式是:
1 | def good_function(new_elem, starter_list=None): |
此处的“好”函数中,增加了第2、3行,就能够保证每次调用函数时,总是一个空列表。
None作为值
什么时候None
可以作为一个有效的输入对象呢?例如在前面定义的good_funciton
函数中,是否可以用None
作为列表中的元素?看看下面的例子:
1 | class DontAppend: pass |
在上面的示例中,DontAppend
类对象并没有追加到列表中,第12行,则实现了将None
作为对象追加到列表中。
另外,None
可以作为返回值。例如,对于dict.get
方法,如果没有指定key
的值,则会返回None
,这时None
就是一个有效的值了,例如:
1 | class KeyNotFound: pass |
None是一种对象
前面提到过,在某些语言中,null
只是0
的符号表示,但是,在Python中,None
是一类对象,即NoneType
类型:
1 | None) type( |
它是Python内置的类型之一。
1 | dir(__builtins__) |
None
和True
、False
一样,都是内置的关键词,所以,你不能用下面的方式得到该对象。
1 | __builtins__.ArithmeticError |
None
是一个单例对象,因此NoneType()
的实例还是None
,即Python中只有一个None
。
1 | None)() # Create a new instance my_None = type( |
还可以检验一下它们的内存地址:
1 | None) id( |
从输出结果中可以看出,前面的“两个”None
实为一个对象。
下面的一些操作,都是不被允许的:
1 | None = 5 |
你也不能用NoneType
作为父类来创建子类:
1 | class MyNoneType(type(None)): |
不过,因为它是一种对象,这样做是可以的;
1 | def returns_None() -> None: |
None
是Python中的对象,也是关键词,可以用它表示缺失值。
若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏
关注微信公众号,读文章、听课程,提升技能