Skip to content

Commit 0dfe649

Browse files
authored
gh-141510: Optimize frozendict(frozendict) (#145592)
Return the same object unmodified if it's exactly the frozendict type. Optimize also PyFrozenDict_New(frozendict).
1 parent 1564e23 commit 0dfe649

File tree

3 files changed

+62
-8
lines changed

3 files changed

+62
-8
lines changed

Lib/test/test_capi/test_dict.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,16 @@ def test_frozendict_new(self):
619619
self.assertEqual(dct, frozendict(x=1, y=2))
620620
self.assertIs(type(dct), frozendict)
621621

622+
# PyFrozenDict_New(frozendict) returns the same object unmodified
623+
fd = frozendict(a=1, b=2, c=3)
624+
fd2 = frozendict_new(fd)
625+
self.assertIs(fd2, fd)
626+
627+
fd = FrozenDictSubclass(a=1, b=2, c=3)
628+
fd2 = frozendict_new(fd)
629+
self.assertIsNot(fd2, fd)
630+
self.assertEqual(fd2, fd)
631+
622632
# PyFrozenDict_New(NULL) creates an empty dictionary
623633
dct = frozendict_new(NULL)
624634
self.assertEqual(dct, frozendict())

Lib/test/test_dict.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1829,6 +1829,13 @@ def test_constructor(self):
18291829
with self.assertRaises(TypeError):
18301830
dict.__init__(d, x=1)
18311831

1832+
# Avoid copy if it's frozendict type
1833+
d2 = frozendict(d)
1834+
self.assertIs(d2, d)
1835+
d2 = FrozenDict(d)
1836+
self.assertIsNot(d2, d)
1837+
self.assertEqual(d2, d)
1838+
18321839
def test_copy(self):
18331840
d = frozendict(x=1, y=2)
18341841
d2 = d.copy()

Objects/dictobject.c

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5169,15 +5169,47 @@ dict_vectorcall(PyObject *type, PyObject * const*args,
51695169
return NULL;
51705170
}
51715171

5172-
PyObject *self;
5173-
if (Py_Is((PyTypeObject*)type, &PyFrozenDict_Type)
5174-
|| PyType_IsSubtype((PyTypeObject*)type, &PyFrozenDict_Type))
5175-
{
5176-
self = frozendict_new(_PyType_CAST(type), NULL, NULL);
5172+
PyObject *self = dict_new(_PyType_CAST(type), NULL, NULL);
5173+
if (self == NULL) {
5174+
return NULL;
51775175
}
5178-
else {
5179-
self = dict_new(_PyType_CAST(type), NULL, NULL);
5176+
if (nargs == 1) {
5177+
if (dict_update_arg(self, args[0]) < 0) {
5178+
Py_DECREF(self);
5179+
return NULL;
5180+
}
5181+
args++;
51805182
}
5183+
if (kwnames != NULL) {
5184+
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(kwnames); i++) {
5185+
PyObject *key = PyTuple_GET_ITEM(kwnames, i); // borrowed
5186+
if (PyDict_SetItem(self, key, args[i]) < 0) {
5187+
Py_DECREF(self);
5188+
return NULL;
5189+
}
5190+
}
5191+
}
5192+
return self;
5193+
}
5194+
5195+
static PyObject *
5196+
frozendict_vectorcall(PyObject *type, PyObject * const*args,
5197+
size_t nargsf, PyObject *kwnames)
5198+
{
5199+
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
5200+
if (!_PyArg_CheckPositional("frozendict", nargs, 0, 1)) {
5201+
return NULL;
5202+
}
5203+
5204+
if (nargs == 1 && kwnames == NULL
5205+
&& PyFrozenDict_CheckExact(args[0])
5206+
&& Py_Is((PyTypeObject*)type, &PyFrozenDict_Type))
5207+
{
5208+
// frozendict(frozendict) returns the same object unmodified
5209+
return Py_NewRef(args[0]);
5210+
}
5211+
5212+
PyObject *self = frozendict_new(_PyType_CAST(type), NULL, NULL);
51815213
if (self == NULL) {
51825214
return NULL;
51835215
}
@@ -8171,6 +8203,11 @@ PyObject*
81718203
PyFrozenDict_New(PyObject *iterable)
81728204
{
81738205
if (iterable != NULL) {
8206+
if (PyFrozenDict_CheckExact(iterable)) {
8207+
// PyFrozenDict_New(frozendict) returns the same object unmodified
8208+
return Py_NewRef(iterable);
8209+
}
8210+
81748211
PyObject *args = PyTuple_Pack(1, iterable);
81758212
if (args == NULL) {
81768213
return NULL;
@@ -8228,6 +8265,6 @@ PyTypeObject PyFrozenDict_Type = {
82288265
.tp_alloc = _PyType_AllocNoTrack,
82298266
.tp_new = frozendict_new,
82308267
.tp_free = PyObject_GC_Del,
8231-
.tp_vectorcall = dict_vectorcall,
8268+
.tp_vectorcall = frozendict_vectorcall,
82328269
.tp_version_tag = _Py_TYPE_VERSION_FROZENDICT,
82338270
};

0 commit comments

Comments
 (0)