Skip to main content

Objects/dictobject.c (part 9)

Source:

cpython 3.14 @ ab2d84fe1023/Objects/dictobject.c

This annotation covers dict bulk operations and merge operators. See objects_dictobject8_detail for dict.__getitem__, __setitem__, __delitem__, and split-table layout.

Map

LinesSymbolRole
1-80dict.updateMerge another mapping or iterable of pairs
81-160dict.setdefaultSet key only if absent
161-260dict.__or__ / __ior__d1 | d2 merge operator (Python 3.9+)
261-360dict.copyShallow copy
361-500_PyDict_MergeExInternal merge with conflict detection

Reading

dict.update

// CPython: Objects/dictobject.c:2920 dict_update_common
static int
dict_update_common(PyObject *self, PyObject *args, PyObject *kwds,
const char *methname)
{
/* dict.update([other,] **kwds)
other can be a dict, a mapping with 'keys', or an iterable of (k, v) */
if (args != NULL) {
PyObject *arg = PyTuple_GET_ITEM(args, 0);
if (PyDict_CheckExact(arg)) {
return PyDict_Merge(self, arg, 1); /* 1 = override */
} else if (PyObject_HasAttr(arg, str_keys)) {
return PyDict_Merge(self, arg, 1);
} else {
return PyDict_MergeFromSeq2(self, arg, 1);
}
}
if (kwds && PyDict_GET_SIZE(kwds)) {
return PyDict_Merge(self, kwds, 1);
}
return 0;
}

d.update({'a': 1}) uses PyDict_Merge (fast dict-to-dict). d.update([('a', 1)]) uses PyDict_MergeFromSeq2 (iterate pairs). d.update(a=1) processes the **kwds.

dict.setdefault

// CPython: Objects/dictobject.c:3020 dict_setdefault_impl
static PyObject *
dict_setdefault_impl(PyDictObject *self, PyObject *key, PyObject *default_value)
{
/* Return self[key] if key in self, else self[key] = default and return default */
PyObject *val = PyDict_GetItemWithError((PyObject *)self, key);
if (val == NULL && !PyErr_Occurred()) {
PyDict_SetItem((PyObject *)self, key, default_value);
val = default_value;
}
return Py_XNewRef(val);
}

d.setdefault('key', []).append(x) is the classic pattern for accumulating values. The lookup and insert are not atomic (no locking), but within a single thread it is always correct.

dict.__or__

// CPython: Objects/dictobject.c:3120 dict_or
static PyObject *
dict_or(PyObject *self, PyObject *other)
{
/* {**self, **other} — other's keys win on conflict */
if (!PyDict_Check(self) || !PyDict_Check(other)) return Py_NotImplemented;
PyObject *new = PyDict_Copy(self);
PyDict_Merge(new, other, 1); /* 1 = override */
return new;
}

d1 | d2 returns a new dict with all keys from both, with d2 taking precedence. d1 |= d2 does the merge in place. Added in Python 3.9 (PEP 584).

gopy notes

dict.update is objects.DictUpdate in objects/dict_mutate.go. It dispatches on the input type using objects.IsDict fast path. dict.setdefault is objects.DictSetDefault. dict.__or__ calls objects.DictCopy then objects.DictMerge. _PyDict_MergeEx (for **kwargs duplicate detection) is objects.DictMergeEx which returns an error on duplicate keys.