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

c
// 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_repr returns the interned string "None".
  • none_bool returns 0, so bool(None) is False and if None: is false.
  • none_hash returns the constant 0xFCA86420.
  • tp_richcompare is _Py_BaseObject_RichCompare, i.e. identity-based equality: None == None is True and None == x for x is not None yields NotImplemented, which Python then resolves to False.

The singleton

The single instance is a file-scope static struct:

c
// 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:

c
// 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
#endif

So _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:

c
// 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)
#endif

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

c
// 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:

c
// 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
#endif

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

c
// 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:

c
// 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_INCREF checks _Py_IsImmortal (or compares ob_refcnt against _Py_IMMORTAL_INITIAL_REFCNT on 64-bit builds) and does nothing for None.
  • Py_DECREF likewise returns early for immortal objects, so None is never deallocated through normal refcounting.
  • Py_SET_REFCNT refuses to change an immortal object’s refcount.
  • none_dealloc re-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:

c
// 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:

c
// 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_bool returns 0, so None is falsy and PyObject_IsTrue(None) returns 0 (there is also an explicit fast path for Py_None in PyObject_IsTrue).
  • none_hash returns 0xFCA86420, a fixed “random-looking” value so hash(None) is stable and None works as a dict key.
  • Equality delegates to _Py_BaseObject_RichCompare, which for Py_EQ returns Py_True only when self == other, and otherwise Py_NotImplemented; Python then falls back to identity, giving the usual None == None is True, None == 0 is False.

Memory layout

There is no separate allocation. _Py_NoneStruct is a single PyObject in the data segment:

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

  • None is a single static PyObject (_Py_NoneStruct) whose type is _PyNone_Type; it is never allocated at runtime.
  • _PyObject_HEAD_INIT marks it immortal and statically allocated at compile time, so Py_INCREF/Py_DECREF are no-ops for it.
  • NoneType has no Py_TPFLAGS_BASETYPE, so it cannot be subclassed, and none_new always returns Py_None.
  • Py_None resolves to &_Py_NoneStruct (or the constant table in the limited API); Py_RETURN_NONE avoids an incref on modern builds.
  • None is falsy, hashes to 0xFCA86420, and compares by identity.
  • none_dealloc re-immortalizes instead of freeing, as a guard against refcount bugs.
  • The interpreter has a dedicated PyStackRef_None so None can 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 in Py_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.

PythonCpythonInternalsNonetypeNone