NoneType
在 Python 中是一个非常特殊的类型,其唯一的值是 None
。这个值经常用来表示“无”或“没有值”,在 Python 的许多方面扮演着重要角色。以下是对 NoneType
的实现、原因和细节的详细解释。
1、问题背景
我最近在某个地方读到,Python 中的特殊值 None 是其自己的类(具体地说是 NoneType)的一个单例对象。这解释了很多问题,因为涉及 Python 中的 None 的大多数错误都会产生 AttributeError,而不是产生某种特殊的“NoneError”或类似错误。由于所有这些 AttributeError 都反映了 NoneType 缺少的属性,因此我开始对 NoneType 感到好奇,想知道它有哪些属性,如果有的话。
我决定研究一下这个 NoneType 并了解更多关于它的信息。我一直认为学习新语言特性的最好方法就是使用它,所以我尝试在 IDLE 中实例化 NoneType:
>>> n = NoneType()
这产生了一个错误:
Traceback (most recent call last):
File "<pyshell#0>", line 1, in <module>
n = NoneType()
NameError: name 'NoneType' is not defined
我感到困惑,于是检查了 None 以确认我是否正确地获得了类型名称。果然,
>>> type(None)
<class 'NoneType'>
现在我更加困惑了,于是我快速地谷歌搜索了一下。搜索结果显示,由于某种原因,NoneType 在 Python 3 中被删除了。
好啊,我想,我可以通过将 None 的类型存储在一个变量中来解决这个问题,因为在 Python 中类是对象。这似乎可行:
>>> NoneType = type(None)
>>> n = NoneType()
当我打印 n 时,我得到了我预期的结果:
>>> print(n)
None
但是然后发生了以下情况:
>>> n is None
True
还有:
>>> id(n)
506768776
>>> id(None)
506768776
我的变量 n 等于 None。它不仅仅是与 None 类型相同。它是 None 本身。这并不是我预期的结果。
我尝试使用 dis 来获取有关 NoneType 的更多信息,但是当我调用以下代码时:
>>> dis.dis(type(None))
它没有产生任何输出。
然后,我尝试调查 new 方法,几位用户在评论中提到了这个方法:
dis.dis(type(None).__new__)
这次,我遇到了另一个错误:
Traceback (most recent call last):
File "<pyshell#4>", line 1, in <module>
dis.dis(type(None).__new__)
File "C:\Python33\lib\dis.py", line 59, in dis
type(x).__name__)
TypeError: don't know how to disassemble builtin_function_or_method objects
更多的错误。
以下是我想问的问题:
- 为什么 n 与 None 是完全相同的对象?
- 为什么语言被设计成 n 与 None 是完全相同的对象?
- 如何用 Python 实现这种行为?
2、解决方案
方法 1:
其他答案描述了如何使用 new 来实现单例,但这并不是 None 的实际实现方式(至少在 cPython 中是这样,我没有研究过其他实现)。
尝试通过 type(None)() 创建 None 的实例是一个特殊情况,最终会调用以下 C 函数:
static PyObject *
none_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
{
if (PyTuple_GET_SIZE(args) || (kwargs && PyDict_Size(kwargs))) {
PyErr_SetString(PyExc_TypeError, "NoneType takes no arguments");
return NULL;
}
Py_RETURN_NONE;
}
而 Py_RETURN_NONE 在此处定义:
/*
_Py_NoneStruct is an object of undefined type which can be used in contexts
where NULL (nil) is not suitable (since NULL often means 'error').
Don't forget to apply Py_INCREF() when returning this value!!!
*/
PyAPI_DATA(PyObject) _Py_NoneStruct; /* Don't use this directly */
#define Py_None (&_Py_NoneStruct)
/* Macro for returning Py_None from a function */
#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
将其与创建普通 Python 对象的函数进行对比:
PyObject *
_PyObject_New(PyTypeObject *tp)
{
PyObject *op;
op = (PyObject *) PyObject_MALLOC(_PyObject_SIZE(tp));
if (op == NULL)
return PyErr_NoMemory();
return PyObject_INIT(op, tp);
}
当你创建一个普通对象时,会为该对象分配内存并对其进行初始化。当你尝试创建一个新的 None 实例时,你得到的只是一个指向已存在的 _Py_NoneStruct 的引用。这就是为什么无论你做什么,对 None 的每个引用都是完全相同的原因。
方法 2:
- 为什么 n 与 None 是完全相同的对象?
C 实现保留了一个单例实例。NoneType.new 返回单例实例。
- 为什么语言被设计成 n 与 None 是完全相同的对象?
如果没有单例实例,那么你就不能依靠 x is None 进行检查,因为 is 操作符是基于标识的。虽然 None == None 也为 True,但有可能当 x 不等于 None 时,x == None 也为 True。查看此答案以了解一个示例。
- 如何用 Python 实现这种行为?
你可以通过覆盖 new 来实现此模式。这里有一个简单的示例:
class Singleton(object):
_instance = None
def __new__(cls, *args, **kwargs):
if Singleton._instance is None:
Singleton._instance = object.__new__(cls, *args, **kwargs)
return Singleton._instance
if __name__ == '__main__':
s1 = Singleton()
s2 = Singleton()
print 's1 is s2:', s1 is s2
print 'id(s1):', id(s1)
print 'id(s2):', id(s2)
输出:
s1 is s2: True
id(s1): 4506243152
id(s2): 4506243152
方法 3:
- 为什么 n 与 None 是完全相同的对象?
Python 中的许多不可变对象都被归为一类,包括 None、较小的整数和许多字符串。
示例:
>>> s1='abc'
>>> s2='def'
>>> s3='abc'
>>> id(s1)
4540177408
>>> id(s3)
4540177408 # 注意:与 s1 相同
>>> x=1
>>> y=2
>>> z=1
>>> id(x)
4538711696
>>> id(z)
4538711696 # 注意:与 x 相同
- 为什么语言被设计成 n 与 None 是完全相同的对象?
请参阅上面给出的答案——速度、效率、消除歧义和内存使用情况是将不可变对象归为一类的原因之一。
- 如何用 Python 实现这种行为?
除其他方法外,你可以覆盖 new 以返回相同对象:
class Singleton(object):
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(Singleton, cls).__new__(
cls, *args, **kwargs)
return cls._instance
对于字符串,你可以在 Python 2 中调用 intern,或在 Python 3 中调用 sys.intern。
在设计自己的 API 或函数时,使用 None
作为默认值或返回值需要慎重考虑,确保它在上下文中的意义明确,避免混淆。例如,如果函数通常返回集合类型的值,那么在异常情况下返回 None
可能导致调用者在未进行空值检查的情况下尝试操作结果,从而引发错误。在这种情况下,返回一个空的集合可能是更安全的做法。
总的来说,None
和 NoneType
是 Python 语言中用来表示空值和缺省状态的重要部分,理解它们的工作方式有助于编写更清晰、更健壯的代码。