专栏首页老齐教室理解Python中的NoneType对象

理解Python中的NoneType对象

编译:老齐

在C、Java等类型的语言中,都有null,它常常被定义为与0等效。但是,在Python中并非如此。Python中用关键词None表征null对象,它并不是0,它是Python中的第一类对象。

None是什么

对于函数,如果在函数体中没有return语句,会默认返回None

>>> def has_no_return():
...     pass
>>> has_no_return()
>>> print(has_no_return())
None

如果调用has_no_return()函数,会看到没有任何返回结果,然而,如果用print()函数打印返回结果,会把默认返回的None打印出来。

通常情况None只有在执行了print()之后才会显示。

>>> None
>>> print(None)
None

None本身没有返回结果,很有意思的是,print()自身也没有返回值,如果你打印print()的返回结果,会得到None

>>> print(print("Hello, World!"))
Hello, World!
None

感觉有点奇怪吧,print(print("..."))所显示的None就是里面的print()返回值。

None还常常作为缺失或者默认参数值,例如:

>>> help(list.sort)
Help on method_descriptor:

sort(...)
    L.sort(key=None, reverse=False) -> None -- stable sort *IN PLACE*

在这里,None是参数key的值,也是返回值类型提示。

使用None

通常,None作为返回值或者某些参数的值,比如在正则表达式中,如果没有匹配对象,则返回None

>>> import re
>>> match = re.match(r"Goodbye", "Hello, World!")
>>> if match is None:
...     print("It doesn't match.")
It doesn't match.

第2行要匹配字符串Hello, World,如果不能匹配则返回None,通过这段代码,我们要知道:

  • 判断是否是None对象,应该使用isis not
  • 而不是使用==或者!=

再比如,我们自定义一个对象,分别用两种方式进行判断:

>>> class BrokenComparison:
...     def __eq__(self, other):
...         return True
>>> b = BrokenComparison()
>>> b == None  # 相等判断
True
>>> b is None  # 对象判断
False

显然,第5行,使用==,返回的结果是错误的。只有用is这个对象身份判断的运算符,才能得到正确结果。

None是假,也就意味着not None是真了。

>>> some_result = None
>>> if some_result:
...     print("Got a result!")
... else:
...     print("No result.")
...
No result.

在第2行条件判断中,并没有显式地写some_result is None,这是因为None本身就是假,与之类似,下面的这些对象也是假:

  • 空列表
  • 空字典
  • 空元组
  • 空字符串
  • 0
  • False

在Python中,变量必须与对象关联,None是一类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

在有的语言中,如果定义了变量,但是没有赋值,该变量值会是null。但Python中的变量不能单独存在,如果将变量与None建立引用关系,并非意味着该变量是空。

None作为参数默认值

更多情况下,你看到的可能是以None为默认参数值。比如:

def bad_function(new_elem, starter_list=[]):
    starter_list.append(new_elem)
    return starter_list

这个例子中,函数的参数starter_list的默认值是一个空列表,这种方式不值得提倡,因为它会隐藏着一些BUG。

如果我们先创建了一个列表,并且通过上面的函数,向该类表中追加一个元素,如下:

>>> my_list = ['a', 'b', 'c']
>>> bad_function('d', my_list)
['a', 'b', 'c', 'd']

这没有什么问题,实现了前面的预想。但是,如果不给starter_list提供参数,即使用默认参数,会怎么样?

>>> bad_function('a')
['a']
>>> bad_function('b')
['a', 'b']
>>> bad_function('c')
['a', 'b', 'c']

第1行调用的时候,返回了一个列表,列表中只有一个元素。第3行再次调用的时候,同样也没有给starter_list提供值,它依然应该是原有列表——注意观察定义函数时的参数。但是,返回值是在上一次调用返回结果中增加了新元素。这就是问题所在,每次重新调用此函数,参数starter_list=[]居然不起作用了。

所以,正如函数名字显示的,这样定义的函数不是一个“好”函数。

推荐的定义方式是:

>>> def good_function(new_elem, starter_list=None):
...     if starter_list is None:
...         starter_list = []
...     starter_list.append(new_elem)
...     return starter_list
...
>>> good_function('e', my_list)
['a', 'b', 'c', 'd', 'e']
>>> good_function('a')
['a']
>>> good_function('b')
['b']
>>> good_function('c')
['c']

此处的“好”函数中,增加了第2、3行,就能够保证每次调用函数时,总是一个空列表。

None作为值

什么时候None可以作为一个有效的输入对象呢?例如在前面定义的good_funciton函数中,是否可以用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类对象并没有追加到列表中,第12行,则实现了将None作为对象追加到列表中。

另外,None可以作为返回值。例如,对于dict.get方法,如果没有指定key的值,则会返回None,这时None就是一个有效的值了,例如:

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

None是一种对象

前面提到过,在某些语言中,null只是0的符号表示,但是,在Python中,None是一类对象,即NoneType类型:

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

它是Python内置的类型之一。

>>> dir(__builtins__)
['ArithmeticError', ..., 'None', ..., 'zip']

NoneTrueFalse一样,都是内置的关键词,所以,你不能用下面的方式得到该对象。

>>> __builtins__.ArithmeticError
<class 'ArithmeticError'>
>>> __builtins__.None
  File "<stdin>", line 1
    __builtins__.None
                    ^
SyntaxError: invalid syntax
>>> print(getattr(__builtins__, 'None'))
None

None是一个单例对象,因此NoneType()的实例还是None,即Python中只有一个None

>>> my_None = type(None)()  # Create a new instance
>>> print(my_None)
None
>>> my_None is None
True

还可以检验一下它们的内存地址:

>>> id(None)
4465912088
>>> id(my_None)
4465912088

从输出结果中可以看出,前面的“两个”None实为一个对象。

下面的一些操作,都是不被允许的:

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

你也不能用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

不过,因为它是一种对象,这样做是可以的;

>>> def returns_None() -> None:
...     pass

None是Python中的对象,也是关键词,可以用它表示缺失值。

参考链接:https://realpython.com/null-in-python/

本文分享自微信公众号 - 老齐教室(itdiffer),作者:老齐

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-06-16

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 零基础学习梯度下降算法

    梯度下降法是机器学习中最基本的优化技术之一。那么,什么是梯度? 下降的是什么?我们要优化的是什么?

    老齐
  • 青少年编程:用Python探究数学(4)

    注意第6行到第8行,就是我们已经熟悉的绘制正方形的程序——前面使用过的,放到了这里,但是,注意第6行,相对函数左侧要有四个空格的缩进,后面各行依次缩进。这样,用...

    老齐
  • 写给小白:K近邻算法入门

    本文的代码,均发布到百度AI Studio的在线平台中,关注微信公众号「老齐教室」,并回复:#真实姓名+手机号+‘案例’#,申请加入含有苯问案例的《机器学习案例...

    老齐
  • 一文详尽解释CatBoost

    CatBoost是俄罗斯的搜索巨头Y andex在2017年开源的机器学习库,也是Boosting族算法的一种,同前面介绍过的XGBoost和LightGBM类...

    石晓文
  • 一文详尽系列之CatBoost

    CatBoost是俄罗斯的搜索巨头Y andex在2017年开源的机器学习库,也是Boosting族算法的一种,同前面介绍过的XGBoost和LightGBM类...

    Datawhale
  • 以太坊智能合约OPCODE逆向之理论基础篇

    在我们对etherscan等平台上合约进行安全审查时,常常会遇到没有公布Solidity源代码的合约,只能获取到合约的OPCODE,所以一个智能合约的反编译器对...

    Seebug漏洞平台
  • Python中的None

    表示该值是一个空对象,空值是Python里一个特殊的值,用None表示。None不能理解为0,因为0是有意义的,而None是一个特殊的空值。

    瑞新
  • AkShare-中国宏观-全社会客货运输量

    指以价值量形式表现的邮电通信企业为社会提供各类邮电通信服务的总数量。邮电业务量按专业分类包括函件、包 件、汇票、报刊发行、邮政快件、特快专递、邮政储蓄、集邮、公...

    AkShare
  • execjs使用时异常

    我们execjs除了nodejs我们还需要浏览器环境,我们浏览器上还需要document以及window对象所有呢我们要安装环境

    小小咸鱼YwY
  • Python数据可视化-seaborn Iris鸢尾花数据

    首先介绍一下Iris鸢尾花数据集,内容摘自百度百科:Iris数据集是常用的分类实验数据集,由Fisher, 1936收集整理。“Iris也称鸢尾花卉数据集,是一...

    拓端

扫码关注云+社区

领取腾讯云代金券