NoneType,原因和细节的实现

App*_*ish 9 python singleton python-3.x python-internals nonetype

我最近在某处读过Nonepython 中的特殊值是它自己类的单例对象,具体来说NoneType.这解释了很多,因为涉及Nonepython的大多数错误产生AttributeErrors而不是某些特殊的"NoneError"或其他东西.

由于所有的这些AttributeErrors反映了属性的NoneType缺乏,我开始用什么属性感兴趣NoneType 如有有.

我决定研究这个NoneType并了解更多相关信息.我总是发现学习新语言功能的最佳方法是使用它,所以我尝试NoneType在IDLE中实例化:

>>> n = NoneType()
Run Code Online (Sandbox Code Playgroud)

这产生了一个错误:

Traceback (most recent call last):
File "<pyshell#0>", line 1, in <module>
    n = NoneType()
NameError: name 'NoneType' is not defined
Run Code Online (Sandbox Code Playgroud)

困惑,我检查None了我是否得到了正确的类型名称.果然,

>>> type(None)
<class 'NoneType'>
Run Code Online (Sandbox Code Playgroud)

现在非常困惑,我做了一个快速的谷歌搜索.这表明由于某些原因,在Python 3中以某种方式删除了NoneType.

好吧,不过,哈哈!我可以通过None在变量中存储类型来解决这个问题,因为类是python中的对象.这似乎有效:

>>> NoneType = type(None)
>>> n = NoneType()
Run Code Online (Sandbox Code Playgroud)

当我打印n时,我得到的是我所期待的:

>>> print(n)
None
Run Code Online (Sandbox Code Playgroud)

但后来发生这种情况:

>>> n is None
True
Run Code Online (Sandbox Code Playgroud)

和:

>>> id(n)
506768776
>>> id(None)
506768776
Run Code Online (Sandbox Code Playgroud)

我的变量nIS None.不仅是同一类型None.它是None.这不是我的预期.

我尝试使用dis以获取更多信息NoneType,但是当我打电话时

>>> dis.dis(type(None))
Run Code Online (Sandbox Code Playgroud)

它没有产生任何产出.

然后我尝试调查这个__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
>>> 
Run Code Online (Sandbox Code Playgroud)

更多错误.

这是我的问题:

  • 为什么n完全相同的对象None
  • 为什么设计的语言n与Object完全相同None
  • 如何在python中实现这种行为?

Nar*_*lei 6

其他答案描述了如何用于__new__实现单例,但实际上并不是实现None的方式(至少在cPython中,我没有研究其他实现)。

尝试通过实例创建None的实例type(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;
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

将此与创建普通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);
}
Run Code Online (Sandbox Code Playgroud)

创建普通对象时,将分配并初始化该对象的内存。当您尝试创建的新实例时None,所获得的只是对已经存在的引用_Py_NoneStruct。这就是为什么无论您做什么,对每个引用都None将是完全相同的对象。


Jak*_*obb 5

为什么是n完全相同的对象None

C 实现保留了一个单例实例。 NoneType.__new__正在返回单例实例。

为什么将语言设计为 n 与 Object 完全相同None

如果没有单例实例,那么您就不能依赖检查,x is None因为is操作符是基于身份的。虽然None == None也是True,它可能有x == NoneTrue时候x实际上不是None。有关示例,请参阅此答案

甚至如何在 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)
Run Code Online (Sandbox Code Playgroud)

输出:

s1 是 s2:真实
id(s1):4506243152
id(s2):4506243152

当然,这个简单的例子并没有使创建第二个实例成为不可能