https://github.com/python/cpython/commit/0dfe649400a0b67318169ec813475f4949ad7b69
commit: 0dfe649400a0b67318169ec813475f4949ad7b69
branch: main
author: Victor Stinner <[email protected]>
committer: vstinner <[email protected]>
date: 2026-03-09T15:47:02+01:00
summary:
gh-141510: Optimize frozendict(frozendict) (#145592)
Return the same object unmodified if it's exactly the frozendict
type.
Optimize also PyFrozenDict_New(frozendict).
files:
M Lib/test/test_capi/test_dict.py
M Lib/test/test_dict.py
M Objects/dictobject.c
diff --git a/Lib/test/test_capi/test_dict.py b/Lib/test/test_capi/test_dict.py
index 5bdf74ef73ab54..cd46fea5476ca6 100644
--- a/Lib/test/test_capi/test_dict.py
+++ b/Lib/test/test_capi/test_dict.py
@@ -619,6 +619,16 @@ def test_frozendict_new(self):
self.assertEqual(dct, frozendict(x=1, y=2))
self.assertIs(type(dct), frozendict)
+ # PyFrozenDict_New(frozendict) returns the same object unmodified
+ fd = frozendict(a=1, b=2, c=3)
+ fd2 = frozendict_new(fd)
+ self.assertIs(fd2, fd)
+
+ fd = FrozenDictSubclass(a=1, b=2, c=3)
+ fd2 = frozendict_new(fd)
+ self.assertIsNot(fd2, fd)
+ self.assertEqual(fd2, fd)
+
# PyFrozenDict_New(NULL) creates an empty dictionary
dct = frozendict_new(NULL)
self.assertEqual(dct, frozendict())
diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py
index 45448d1264a53e..b2f4363b23e748 100644
--- a/Lib/test/test_dict.py
+++ b/Lib/test/test_dict.py
@@ -1829,6 +1829,13 @@ def test_constructor(self):
with self.assertRaises(TypeError):
dict.__init__(d, x=1)
+ # Avoid copy if it's frozendict type
+ d2 = frozendict(d)
+ self.assertIs(d2, d)
+ d2 = FrozenDict(d)
+ self.assertIsNot(d2, d)
+ self.assertEqual(d2, d)
+
def test_copy(self):
d = frozendict(x=1, y=2)
d2 = d.copy()
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
index 61fde37f8d4fff..b5f2a682c54982 100644
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -5169,15 +5169,47 @@ dict_vectorcall(PyObject *type, PyObject * const*args,
return NULL;
}
- PyObject *self;
- if (Py_Is((PyTypeObject*)type, &PyFrozenDict_Type)
- || PyType_IsSubtype((PyTypeObject*)type, &PyFrozenDict_Type))
- {
- self = frozendict_new(_PyType_CAST(type), NULL, NULL);
+ PyObject *self = dict_new(_PyType_CAST(type), NULL, NULL);
+ if (self == NULL) {
+ return NULL;
}
- else {
- self = dict_new(_PyType_CAST(type), NULL, NULL);
+ if (nargs == 1) {
+ if (dict_update_arg(self, args[0]) < 0) {
+ Py_DECREF(self);
+ return NULL;
+ }
+ args++;
}
+ if (kwnames != NULL) {
+ for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(kwnames); i++) {
+ PyObject *key = PyTuple_GET_ITEM(kwnames, i); // borrowed
+ if (PyDict_SetItem(self, key, args[i]) < 0) {
+ Py_DECREF(self);
+ return NULL;
+ }
+ }
+ }
+ return self;
+}
+
+static PyObject *
+frozendict_vectorcall(PyObject *type, PyObject * const*args,
+ size_t nargsf, PyObject *kwnames)
+{
+ Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
+ if (!_PyArg_CheckPositional("frozendict", nargs, 0, 1)) {
+ return NULL;
+ }
+
+ if (nargs == 1 && kwnames == NULL
+ && PyFrozenDict_CheckExact(args[0])
+ && Py_Is((PyTypeObject*)type, &PyFrozenDict_Type))
+ {
+ // frozendict(frozendict) returns the same object unmodified
+ return Py_NewRef(args[0]);
+ }
+
+ PyObject *self = frozendict_new(_PyType_CAST(type), NULL, NULL);
if (self == NULL) {
return NULL;
}
@@ -8171,6 +8203,11 @@ PyObject*
PyFrozenDict_New(PyObject *iterable)
{
if (iterable != NULL) {
+ if (PyFrozenDict_CheckExact(iterable)) {
+ // PyFrozenDict_New(frozendict) returns the same object unmodified
+ return Py_NewRef(iterable);
+ }
+
PyObject *args = PyTuple_Pack(1, iterable);
if (args == NULL) {
return NULL;
@@ -8228,6 +8265,6 @@ PyTypeObject PyFrozenDict_Type = {
.tp_alloc = _PyType_AllocNoTrack,
.tp_new = frozendict_new,
.tp_free = PyObject_GC_Del,
- .tp_vectorcall = dict_vectorcall,
+ .tp_vectorcall = frozendict_vectorcall,
.tp_version_tag = _Py_TYPE_VERSION_FROZENDICT,
};
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]