Skip to main content

Python/ceval.c (part 45)

Source:

cpython 3.14 @ ab2d84fe1023/Python/ceval.c

This annotation covers subscript specializations. See python_ceval44_detail for STORE_ATTR specializations.

Map

LinesSymbolRole
1-80BINARY_SUBSCR_LIST_INTlist[int] fast path
81-160BINARY_SUBSCR_TUPLE_INTtuple[int] fast path
161-240BINARY_SUBSCR_DICTDict subscript with cached hash
241-320STORE_SUBSCR_LIST_INTlist[int] = value fast path
321-500STORE_SUBSCR_DICTDict assignment

Reading

BINARY_SUBSCR_LIST_INT

// CPython: Python/ceval.c:5740 BINARY_SUBSCR_LIST_INT
inst(BINARY_SUBSCR_LIST_INT, (list, sub -- res)) {
DEOPT_IF(!PyList_CheckExact(list), BINARY_SUBSCR);
DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR);
Py_ssize_t index = _PyLong_IsCompact((PyLongObject *)sub) ?
_PyLong_CompactValue((PyLongObject *)sub) : -1;
DEOPT_IF(index < 0, BINARY_SUBSCR);
DEOPT_IF(index >= PyList_GET_SIZE(list), BINARY_SUBSCR);
res = Py_NewRef(PyList_GET_ITEM(list, index));
Py_DECREF(list);
Py_DECREF(sub);
}

my_list[0] avoids the generic __getitem__ dispatch. The type check (PyList_CheckExact), range check, and item access are all in a handful of instructions. Compact integer check ensures index fits in a machine word.

BINARY_SUBSCR_DICT

// CPython: Python/ceval.c:5820 BINARY_SUBSCR_DICT
inst(BINARY_SUBSCR_DICT, (dict, sub -- res)) {
DEOPT_IF(!PyDict_CheckExact(dict), BINARY_SUBSCR);
/* Use precomputed hash from cache if available */
Py_hash_t hash;
if (PyUnicode_CheckExact(sub)) {
hash = unicode_get_hash(sub);
if (hash == -1) hash = PyObject_Hash(sub);
} else {
hash = PyObject_Hash(sub);
}
res = _PyDict_GetItem_KnownHash(dict, sub, hash);
if (res == NULL) {
if (!PyErr_Occurred())
_PyErr_SetKeyError(sub);
goto error;
}
res = Py_NewRef(res);
Py_DECREF(dict);
Py_DECREF(sub);
}

my_dict['key'] specializes with BINARY_SUBSCR_DICT. For string keys with a cached hash (_PyASCIIObject.hash), the hash lookup avoids recomputation on repeated access. This is significant for hot loops like config['key'].

STORE_SUBSCR_LIST_INT

// CPython: Python/ceval.c:5880 STORE_SUBSCR_LIST_INT
inst(STORE_SUBSCR_LIST_INT, (value, list, sub --)) {
/* list[int] = value */
DEOPT_IF(!PyList_CheckExact(list), STORE_SUBSCR);
DEOPT_IF(!PyLong_CheckExact(sub), STORE_SUBSCR);
Py_ssize_t index = ...;
DEOPT_IF(index < 0 || index >= PyList_GET_SIZE(list), STORE_SUBSCR);
PyObject *old = PyList_GET_ITEM(list, index);
PyList_SET_ITEM(list, index, Py_NewRef(value));
Py_DECREF(old);
Py_DECREF(list);
Py_DECREF(sub);
}

my_list[i] = v with a known-integer index directly updates ob_item[i]. The old value is decremented, the new value is increfed and stored. No bounds check expansion (the deopt guard handles it).

gopy notes

BINARY_SUBSCR_LIST_INT is in vm/eval_simple.go; it accesses objects.List.Items[index] directly. BINARY_SUBSCR_DICT calls objects.DictGetKnownHash. STORE_SUBSCR_LIST_INT sets objects.List.Items[index]. STORE_SUBSCR_DICT calls objects.DictSetItem.