CPython's NoneType Implementation in C (None)
The None singleton as a static PyObject, _Py_NoneStruct, _PyNone_Type, immortality, refcount special-casing, and Py_RETURN_NONE traced through CPython 3.15.
None is Python’s “no value” sentinel. There is exactly one instance of
NoneType, it is immortal, it has no methods, and comparing it is just
pointer equality. In CPython all of that is implemented in Objects/object.c
by storing a single static PyObject called _Py_NoneStruct whose type is
_PyNone_Type. This article traces that implementation through the 3.15
source.
The type object
_PyNone_Type is a plain PyTypeObject with no base class and no
Py_TPFLAGS_BASETYPE, so NoneType cannot be subclassed (just like
bool):
// Objects/object.c
PyTypeObject _PyNone_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"NoneType",
0, /* tp_basicsize */
0, /* tp_itemsize */
none_dealloc, /* tp_dealloc */
...
none_repr, /* tp_repr */
&none_as_number, /* tp_as_number */
...
none_hash, /* tp_hash */
...
Py_TPFLAGS_DEFAULT, /* tp_flags */
none_doc, /* tp_doc */
...
_Py_BaseObject_RichCompare, /* tp_richcompare */
...
0, /* tp_init */
0, /* tp_alloc */
none_new, /* tp_new */
};The instance payload is just the PyObject header (refcount + type), so
tp_basicsize is 0 and no per-instance data is ever allocated. The
interesting behaviour is concentrated in a handful of slots:
none_reprreturns the interned string"None".none_boolreturns0, sobool(None)isFalseandif None:is false.none_hashreturns the constant0xFCA86420.tp_richcompareis_Py_BaseObject_RichCompare, i.e. identity-based equality:None == NoneisTrueandNone == xforx is not NoneyieldsNotImplemented, which Python then resolves toFalse.
The singleton
The single instance is a file-scope static struct:
// Objects/object.c
PyObject _Py_NoneStruct = _PyObject_HEAD_INIT(&_PyNone_Type);_PyObject_HEAD_INIT (the internal variant, used for objects that must be
immortal and statically allocated) initialises the header with an immortal
refcount and the static-allocation flag:
// Include/internal/pycore_object.h
#if defined(Py_GIL_DISABLED)
#define _PyObject_HEAD_INIT(type) \
{ \
.ob_ref_local = _Py_IMMORTAL_REFCNT_LOCAL, \
.ob_flags = _Py_STATICALLY_ALLOCATED_FLAG, \
.ob_gc_bits = _PyGC_BITS_DEFERRED, \
.ob_type = (type) \
}
#else
#if SIZEOF_VOID_P > 4
#define _PyObject_HEAD_INIT(type) \
{ \
.ob_refcnt = _Py_IMMORTAL_INITIAL_REFCNT, \
.ob_flags = _Py_STATIC_FLAG_BITS, \
.ob_type = (type) \
}
#else
#define _PyObject_HEAD_INIT(type) \
{ \
.ob_refcnt = _Py_STATIC_IMMORTAL_INITIAL_REFCNT, \
.ob_type = (type) \
}
#endif
#endifSo _Py_NoneStruct is never malloc’d. It lives in the data segment for the
lifetime of the process, and its refcount is initialised to an immortal value
rather than 1.
Py_None and the public macro
Py_None is just the address of that struct in the non-limited API:
// Include/object.h
PyAPI_DATA(PyObject) _Py_NoneStruct; /* Don't use this directly */
#if defined(Py_LIMITED_API) && Py_LIMITED_API+0 >= 0x030D0000
# define Py_None Py_GetConstantBorrowed(Py_CONSTANT_NONE)
#else
# define Py_None (&_Py_NoneStruct)
#endifIn the limited/abi3 case, Py_None is fetched from the interpreter’s
constant table instead (see below), which keeps the singleton identity stable
across ABI versions while hiding the struct.
none_new: always the singleton
NoneType() (and NoneType(x)) is implemented by none_new. It rejects
arguments and returns the singleton:
// Objects/object.c
static PyObject *
none_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
{
if (PyTuple_GET_SIZE(args) || (kwargs && PyDict_GET_SIZE(kwargs))) {
PyErr_SetString(PyExc_TypeError, "NoneType takes no arguments");
return NULL;
}
Py_RETURN_NONE;
}Py_RETURN_NONE itself is a refcount-aware macro:
// Include/object.h
#if defined(Py_LIMITED_API) && Py_LIMITED_API+0 < 0x030c0000
# define Py_RETURN_NONE return Py_NewRef(Py_None)
#else
# define Py_RETURN_NONE return Py_None
#endifOn modern builds Py_None is immortal, so Py_RETURN_NONE is just
return Py_None with no incref. On older limited-API builds it takes a new
reference explicitly, because Py_None was not yet guaranteed immortal there.
none_dealloc: the belt-and-braces guard
Because None is immortal, none_dealloc should never run. It exists only to
avoid a segfault if a refcount bug ever drives it to zero, and it
re-immortalizes the object instead of freeing it:
// Objects/object.c
static void
none_dealloc(PyObject* none)
{
/* This should never get called, but we also don't want to SEGV if
* we accidentally decref None out of existence. Instead,
* since None is an immortal object, re-set the reference count.
*/
_Py_SetImmortal(none);
}_Py_SetImmortal (and the underlying _Py_SetImmortalUntracked) reset
ob_refcnt/ob_ref_local to an immortal value and clear the GC-tracked bit,
so a stray dealloc resurrects None rather than corrupting memory.
The constants table
None also appears in the interpreter’s immortal-constant table, which is what
Py_GetConstant / Py_GetConstantBorrowed and Py_CONSTANT_NONE resolve to:
// Objects/object.c
static PyObject* constants[] = {
&_Py_NoneStruct, // Py_CONSTANT_NONE
(PyObject*)(&_Py_FalseStruct), // Py_CONSTANT_FALSE
(PyObject*)(&_Py_TrueStruct), // Py_CONSTANT_TRUE
&_Py_EllipsisObject, // Py_CONSTANT_ELLIPSIS
&_Py_NotImplementedStruct, // Py_CONSTANT_NOT_IMPLEMENTED
NULL, // Py_CONSTANT_ZERO
...
};
PyObject*
Py_GetConstant(unsigned int constant_id)
{
if (constant_id < Py_ARRAY_LENGTH(constants)) {
return constants[constant_id];
}
else {
PyErr_BadInternalCall();
return NULL;
}
}
PyObject*
Py_GetConstantBorrowed(unsigned int constant_id)
{
// All constants are immortal
return Py_GetConstant(constant_id);
}_Py_GetConstant_Init asserts every entry is immortal, so a non-immortal None
would fail an assertion at startup.
Refcount special-casing
None is a static immortal object, and the refcount machinery treats such objects specially:
Py_INCREFchecks_Py_IsImmortal(or comparesob_refcntagainst_Py_IMMORTAL_INITIAL_REFCNTon 64-bit builds) and does nothing for None.Py_DECREFlikewise returns early for immortal objects, so None is never deallocated through normal refcounting.Py_SET_REFCNTrefuses to change an immortal object’s refcount.none_deallocre-immortalizes as a last resort.
On free-threaded builds (Py_GIL_DISABLED), None uses ob_ref_local == _Py_IMMORTAL_REFCNT_LOCAL, the sentinel value that means “immortal” in the
local/shared refcount scheme.
There is also a stack-reference special case: the interpreter’s _PyStackRef
encoding tags pointers with Py_TAG_REFCNT, and the singleton has its own
constant:
// Include/internal/pycore_stackref.h
#define PyStackRef_None ((_PyStackRef){.bits = ((uintptr_t)&_Py_NoneStruct) | Py_TAG_REFCNT })
...
#define PyStackRef_IsNone(REF) ((REF).bits == (((uintptr_t)&_Py_NoneStruct) | Py_TAG_REFCNT))This lets the eval loop push/pop None on the value stack without touching
its refcount at all.
Py_IsNone and identity checks
There is no PyNone_Check because identity is sufficient. The stable ABI
exports Py_IsNone (and the Py_IsNone(x) macro) as a thin wrapper over
pointer equality:
// Objects/object.c / Include/object.h
int Py_IsNone(PyObject *x)
{
return Py_Is(x, Py_None);
}
#define Py_IsNone(x) Py_Is((x), Py_None)So x is None in Python compiles down to a pointer comparison against
&_Py_NoneStruct (or against the constant-table entry), with no type check
and no refcount change.
Hashing, truthiness, and equality
none_boolreturns0, soNoneis falsy andPyObject_IsTrue(None)returns0(there is also an explicit fast path forPy_NoneinPyObject_IsTrue).none_hashreturns0xFCA86420, a fixed “random-looking” value sohash(None)is stable andNoneworks as a dict key.- Equality delegates to
_Py_BaseObject_RichCompare, which forPy_EQreturnsPy_Trueonly whenself == other, and otherwisePy_NotImplemented; Python then falls back to identity, giving the usualNone == NoneisTrue,None == 0isFalse.
Memory layout
There is no separate allocation. _Py_NoneStruct is a single PyObject in
the data segment:
_Py_NoneStruct (static, immortal)
+---------------------------+
| ob_refcnt (immortal) |
+---------------------------+
| *ob_type (&_PyNone_Type) |
+---------------------------+_PyNone_Type.tp_basicsize is 0 because the instance needs no payload
beyond the header, and tp_itemsize is 0. The object is GC-immortal and
statically allocated, so it is never tracked by the cyclic garbage collector.
Key takeaways
Noneis a single staticPyObject(_Py_NoneStruct) whose type is_PyNone_Type; it is never allocated at runtime._PyObject_HEAD_INITmarks it immortal and statically allocated at compile time, soPy_INCREF/Py_DECREFare no-ops for it.NoneTypehas noPy_TPFLAGS_BASETYPE, so it cannot be subclassed, andnone_newalways returnsPy_None.Py_Noneresolves to&_Py_NoneStruct(or the constant table in the limited API);Py_RETURN_NONEavoids an incref on modern builds.Noneis falsy, hashes to0xFCA86420, and compares by identity.none_deallocre-immortalizes instead of freeing, as a guard against refcount bugs.- The interpreter has a dedicated
PyStackRef_NonesoNonecan be pushed on the value stack without refcount changes.
Further reading
Objects/object.c–_PyNone_Type,_Py_NoneStruct,none_new,none_dealloc,none_bool,none_hash, constants table,Py_IsNone.Include/object.h–Py_None,Py_RETURN_NONE,Py_IsNone.Include/internal/pycore_object.h–_PyObject_HEAD_INIT,_Py_SetImmortal.Include/refcount.h– immortal refcount checks inPy_INCREF/Py_DECREF.Include/internal/pycore_stackref.h–PyStackRef_None.Objects/typeobject.c–_Py_BaseObject_RichCompare.Doc/c-api/none.rst– public C API documentation.