Skip to content

Commit bfd6296

Browse files
author
rhettinger
committed
* Improve code for the empty frozenset singleton:
- Handle both frozenset() and frozenset([]). - Do not use singleton for frozenset subclasses. - Finalize the singleton. - Add test cases. * Factor-out set_update_internal() from set_update(). Simplifies the code for several internal callers. * Factor constant expressions out of loop in set_merge_internal(). * Minor comment touch-ups. git-svn-id: http://svn.python.org/projects/python/trunk@39238 6015fed2-1504-0410-9fe1-9d1591cc4771
1 parent 8891863 commit bfd6296

5 files changed

Lines changed: 89 additions & 65 deletions

File tree

Include/pythonrun.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ PyAPI_FUNC(void) PyFrame_Fini(void);
115115
PyAPI_FUNC(void) PyCFunction_Fini(void);
116116
PyAPI_FUNC(void) PyTuple_Fini(void);
117117
PyAPI_FUNC(void) PyList_Fini(void);
118+
PyAPI_FUNC(void) PySet_Fini(void);
118119
PyAPI_FUNC(void) PyString_Fini(void);
119120
PyAPI_FUNC(void) PyInt_Fini(void);
120121
PyAPI_FUNC(void) PyFloat_Fini(void);

Include/setobject.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,7 @@ struct _setobject {
4242

4343
/* table points to smalltable for small tables, else to
4444
* additional malloc'ed memory. table is never NULL! This rule
45-
* saves repeated runtime null-tests in the workhorse getitem and
46-
* setitem calls.
45+
* saves repeated runtime null-tests.
4746
*/
4847
setentry *table;
4948
setentry *(*lookup)(PySetObject *so, PyObject *key, long hash);

Lib/test/test_set.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,15 @@ def test_init(self):
391391
s.__init__(self.otherword)
392392
self.assertEqual(s, set(self.word))
393393

394+
def test_singleton_empty_frozenset(self):
395+
f = frozenset()
396+
efs = [frozenset(), frozenset([]), frozenset(()), frozenset(''),
397+
frozenset(), frozenset([]), frozenset(()), frozenset(''),
398+
frozenset(xrange(0)), frozenset(frozenset()),
399+
frozenset(f), f]
400+
# All of the empty frozensets should have just one id()
401+
self.assertEqual(len(set(map(id, efs))), 1)
402+
394403
def test_constructor_identity(self):
395404
s = self.thetype(range(3))
396405
t = self.thetype(s)
@@ -456,6 +465,17 @@ def test_nested_empty_constructor(self):
456465
t = self.thetype(s)
457466
self.assertEqual(s, t)
458467

468+
def test_singleton_empty_frozenset(self):
469+
Frozenset = self.thetype
470+
f = frozenset()
471+
F = Frozenset()
472+
efs = [Frozenset(), Frozenset([]), Frozenset(()), Frozenset(''),
473+
Frozenset(), Frozenset([]), Frozenset(()), Frozenset(''),
474+
Frozenset(xrange(0)), Frozenset(Frozenset()),
475+
Frozenset(frozenset()), f, F, Frozenset(f), Frozenset(F)]
476+
# All empty frozenset subclass instances should have different ids
477+
self.assertEqual(len(set(map(id, efs))), len(efs))
478+
459479
# Tests taken from test_sets.py =============================================
460480

461481
empty_set = set()

Objects/setobject.c

Lines changed: 66 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -311,9 +311,6 @@ set_table_resize(PySetObject *so, int minused)
311311
return 0;
312312
}
313313

314-
/*** Internal functions (derived from public dictionary api functions ) ***/
315-
316-
317314
/* CAUTION: set_add_internal() must guarantee that it won't resize the table */
318315
static int
319316
set_add_internal(register PySetObject *so, PyObject *key)
@@ -435,10 +432,10 @@ set_clear_internal(PySetObject *so)
435432
/*
436433
* Iterate over a set table. Use like so:
437434
*
438-
* int i;
435+
* int pos;
439436
* PyObject *key;
440-
* i = 0; # important! i should not otherwise be changed by you
441-
* while (set_next_internal(yourset, &i, &key)) {
437+
* pos = 0; # important! pos should not otherwise be changed by you
438+
* while (set_next_internal(yourset, &pos, &key)) {
442439
* Refer to borrowed reference in key.
443440
* }
444441
*
@@ -467,14 +464,13 @@ set_next_internal(PySetObject *so, int *pos, PyObject **key)
467464
return 1;
468465
}
469466

470-
/* Methods */
471-
472467
static int
473468
set_merge_internal(PySetObject *so, PyObject *otherset)
474469
{
475-
register PySetObject *other;
470+
PySetObject *other;
476471
register int i;
477-
setentry *entry;
472+
register setentry *entry, *othertable;
473+
register int othermask;
478474

479475
assert (PyAnySet_Check(so));
480476
assert (PyAnySet_Check(otherset));
@@ -491,8 +487,10 @@ set_merge_internal(PySetObject *so, PyObject *otherset)
491487
if (set_table_resize(so, (so->used + other->used)*2) != 0)
492488
return -1;
493489
}
494-
for (i = 0; i <= other->mask; i++) {
495-
entry = &other->table[i];
490+
othermask = other->mask;
491+
othertable = other->table;
492+
for (i = 0; i <= othermask; i++) {
493+
entry = &othertable[i];
496494
if (entry->key != NULL &&
497495
entry->key != dummy) {
498496
Py_INCREF(entry->key);
@@ -517,9 +515,9 @@ set_contains_internal(PySetObject *so, PyObject *key)
517515
return key != NULL && key != dummy;
518516
}
519517

520-
static PyTypeObject PySetIter_Type; /* Forward */
518+
/***** Set iterator types **********************************************/
521519

522-
/* Set iterator types */
520+
static PyTypeObject PySetIter_Type; /* Forward */
523521

524522
typedef struct {
525523
PyObject_HEAD
@@ -647,41 +645,52 @@ static PyTypeObject PySetIter_Type = {
647645
All rights reserved.
648646
*/
649647

650-
static PyObject *
651-
set_update(PySetObject *so, PyObject *other)
648+
static int
649+
set_len(PyObject *so)
650+
{
651+
return ((PySetObject *)so)->used;
652+
}
653+
654+
static int
655+
set_update_internal(PySetObject *so, PyObject *other)
652656
{
653657
PyObject *key, *it;
654658

655-
if (PyAnySet_Check(other)) {
656-
if (set_merge_internal(so, other) == -1)
657-
return NULL;
658-
Py_RETURN_NONE;
659-
}
659+
if (PyAnySet_Check(other))
660+
return set_merge_internal(so, other);
660661

661662
if (PyDict_Check(other)) {
662663
PyObject *key, *value;
663664
int pos = 0;
664665
while (PyDict_Next(other, &pos, &key, &value)) {
665666
if (set_add_internal(so, key) == -1)
666-
return NULL;
667+
return -1;
667668
}
668-
Py_RETURN_NONE;
669+
return 0;
669670
}
670671

671672
it = PyObject_GetIter(other);
672673
if (it == NULL)
673-
return NULL;
674+
return -1;
674675

675676
while ((key = PyIter_Next(it)) != NULL) {
676677
if (set_add_internal(so, key) == -1) {
677678
Py_DECREF(it);
678679
Py_DECREF(key);
679-
return NULL;
680+
return -1;
680681
}
681682
Py_DECREF(key);
682683
}
683684
Py_DECREF(it);
684685
if (PyErr_Occurred())
686+
return -1;
687+
return 0;
688+
}
689+
690+
static PyObject *
691+
set_update(PySetObject *so, PyObject *other)
692+
{
693+
if (set_update_internal(so, other) == -1)
685694
return NULL;
686695
Py_RETURN_NONE;
687696
}
@@ -692,7 +701,6 @@ PyDoc_STRVAR(update_doc,
692701
static PyObject *
693702
make_new_set(PyTypeObject *type, PyObject *iterable)
694703
{
695-
PyObject *tmp;
696704
register PySetObject *so = NULL;
697705

698706
if (dummy == NULL) { /* Auto-initialize dummy */
@@ -712,37 +720,51 @@ make_new_set(PyTypeObject *type, PyObject *iterable)
712720
so->weakreflist = NULL;
713721

714722
if (iterable != NULL) {
715-
tmp = set_update(so, iterable);
716-
if (tmp == NULL) {
723+
if (set_update_internal(so, iterable) == -1) {
717724
Py_DECREF(so);
718725
return NULL;
719726
}
720-
Py_DECREF(tmp);
721727
}
722728

723729
return (PyObject *)so;
724730
}
725731

732+
/* The empty frozenset is a singleton */
733+
static PyObject *emptyfrozenset = NULL;
734+
726735
static PyObject *
727736
frozenset_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
728737
{
729-
PyObject *iterable = NULL;
730-
static PyObject *emptyfrozenset = NULL;
738+
PyObject *iterable = NULL, *result;
731739

732740
if (!PyArg_UnpackTuple(args, type->tp_name, 0, 1, &iterable))
733741
return NULL;
734-
if (iterable == NULL) {
735-
if (type == &PyFrozenSet_Type) {
736-
if (emptyfrozenset == NULL)
737-
emptyfrozenset = make_new_set(type, NULL);
738-
Py_INCREF(emptyfrozenset);
739-
return emptyfrozenset;
742+
743+
if (type != &PyFrozenSet_Type)
744+
return make_new_set(type, iterable);
745+
746+
if (iterable != NULL) {
747+
/* frozenset(f) is idempotent */
748+
if (PyFrozenSet_CheckExact(iterable)) {
749+
Py_INCREF(iterable);
750+
return iterable;
740751
}
741-
} else if (PyFrozenSet_CheckExact(iterable)) {
742-
Py_INCREF(iterable);
743-
return iterable;
752+
result = make_new_set(type, iterable);
753+
if (result == NULL || set_len(result))
754+
return result;
755+
Py_DECREF(result);
744756
}
745-
return make_new_set(type, iterable);
757+
/* The empty frozenset is a singleton */
758+
if (emptyfrozenset == NULL)
759+
emptyfrozenset = make_new_set(type, NULL);
760+
Py_XINCREF(emptyfrozenset);
761+
return emptyfrozenset;
762+
}
763+
764+
void
765+
PySet_Fini(void)
766+
{
767+
Py_XDECREF(emptyfrozenset);
746768
}
747769

748770
static PyObject *
@@ -786,12 +808,6 @@ set_traverse(PySetObject *so, visitproc visit, void *arg)
786808
return 0;
787809
}
788810

789-
static int
790-
set_len(PyObject *so)
791-
{
792-
return ((PySetObject *)so)->used;
793-
}
794-
795811
/* set_swap_bodies() switches the contents of any two sets by moving their
796812
internal data pointers and, if needed, copying the internal smalltables.
797813
Semantically equivalent to:
@@ -892,17 +908,14 @@ static PyObject *
892908
set_union(PySetObject *so, PyObject *other)
893909
{
894910
PySetObject *result;
895-
PyObject *rv;
896911

897912
result = (PySetObject *)set_copy(so);
898913
if (result == NULL)
899914
return NULL;
900-
rv = set_update(result, other);
901-
if (rv == NULL) {
915+
if (set_update_internal(result, other) == -1) {
902916
Py_DECREF(result);
903917
return NULL;
904918
}
905-
Py_DECREF(rv);
906919
return (PyObject *)result;
907920
}
908921

@@ -924,16 +937,12 @@ set_or(PySetObject *so, PyObject *other)
924937
static PyObject *
925938
set_ior(PySetObject *so, PyObject *other)
926939
{
927-
PyObject *result;
928-
929940
if (!PyAnySet_Check(other)) {
930941
Py_INCREF(Py_NotImplemented);
931942
return Py_NotImplemented;
932943
}
933-
result = set_update(so, other);
934-
if (result == NULL)
944+
if (set_update_internal(so, other) == -1)
935945
return NULL;
936-
Py_DECREF(result);
937946
Py_INCREF(so);
938947
return (PyObject *)so;
939948
}
@@ -1553,7 +1562,6 @@ static int
15531562
set_init(PySetObject *self, PyObject *args, PyObject *kwds)
15541563
{
15551564
PyObject *iterable = NULL;
1556-
PyObject *result;
15571565

15581566
if (!PyAnySet_Check(self))
15591567
return -1;
@@ -1563,12 +1571,7 @@ set_init(PySetObject *self, PyObject *args, PyObject *kwds)
15631571
self->hash = -1;
15641572
if (iterable == NULL)
15651573
return 0;
1566-
result = set_update(self, iterable);
1567-
if (result != NULL) {
1568-
Py_DECREF(result);
1569-
return 0;
1570-
}
1571-
return -1;
1574+
return set_update_internal(self, iterable);
15721575
}
15731576

15741577
static PySequenceMethods set_as_sequence = {

Python/pythonrun.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,7 @@ Py_Finalize(void)
420420
PyCFunction_Fini();
421421
PyTuple_Fini();
422422
PyList_Fini();
423+
PySet_Fini();
423424
PyString_Fini();
424425
PyInt_Fini();
425426
PyFloat_Fini();

0 commit comments

Comments
 (0)