Add a C API for sets and frozensets.

This commit is contained in:
Raymond Hettinger 2005-08-16 03:47:52 +00:00
parent e2eca0b709
commit beb3101b05
5 changed files with 210 additions and 12 deletions

View File

@ -2897,3 +2897,128 @@ Macros for the convenience of modules implementing the DB API:
tuple suitable for passing to \code{datetime.date.fromtimestamp()}.
\versionadded{2.4}
\end{cfuncdesc}
\subsection{Set Objects \label{setObjects}}
\sectionauthor{Raymond D. Hettinger}{python@rcn.com}
\obindex{set}
\obindex{frozenset}
\versionadded{2.5}
This section details the public API for \class{set} and \class{frozenset}
objects. Any functionality not listed below is best accessed using the
abstract object API (including
\cfunction{PyObject_CallMethod()}, \cfunction{PyObject_RichCompareBool()},
\cfunction{PyObject_Hash()}, \cfunction{PyObject_Repr()},
\cfunction{PyObject_IsTrue()}, \cfunction{PyObject_Print()}, and
\cfunction{PyObject_GetIter()}).
\begin{ctypedesc}{PySetObject}
This subtype of \ctype{PyObject} is used to hold the internal data for
both \class{set} and \class{frozenset} objects. It is like a
\ctype{PyDictObject} in that it is a fixed size for small sets
(much like tuple storage) and will point to a separate, variable sized
block of memory for medium and large sized sets (much like list storage).
None of the fields of this structure should be considered public and
are subject to change. All access should be done through the
documented API.
\end{ctypedesc}
\begin{cvardesc}{PyTypeObject}{PySet_Type}
This is an instance of \ctype{PyTypeObject} representing the Python
\class{set} type.
\end{cvardesc}
\begin{cvardesc}{PyTypeObject}{PyFrozenSet_Type}
This is an instance of \ctype{PyTypeObject} representing the Python
\class{frozenset} type.
\end{cvardesc}
The following type check macros work on pointers to any Python object.
Likewise, the constructor functions work with any iterable Python object.
\begin{cfuncdesc}{int}{PyAnySet_Check}{PyObject *p}
Returns true if \var{p} is a \class{set} object, a \class{frozenset}
object, or an instance of a subtype.
\end{cfuncdesc}
\begin{cfuncdesc}{int}{PyAnySet_CheckExact}{PyObject *p}
Returns true if \var{p} is a \class{set} object or a \class{frozenset}
object but not an instance of a subtype.
\end{cfuncdesc}
\begin{cfuncdesc}{int}{PyFrozenSet_CheckExact}{PyObject *p}
Returns true if \var{p} is a \class{frozenset} object
but not an instance of a subtype.
\end{cfuncdesc}
\begin{cfuncdesc}{PyObject*}{PySet_New}{PyObject *iterable}
Returns a new \class{set} containing objects returned by the
\var{iterable}. The \var{iterable} may be \NULL{} to create a
new empty set. Returns the new set on success or \NULL{} on
failure.
\end{cfuncdesc}
\begin{cfuncdesc}{PyObject*}{PyFrozenSet_New}{PyObject *iterable}
Returns a new \class{frozenset} containing objects returned by the
\var{iterable}. The \var{iterable} may be \NULL{} to create a
new empty frozenset. Returns the new set on success or \NULL{} on
failure.
\end{cfuncdesc}
The following functions and macros are available for instances of
\class{set} or \class{frozenset} or instances of their subtypes.
\begin{cfuncdesc}{int}{PySet_Size}{PyObject *anyset}
Returns the length of a \class{set} or \class{frozenset} object.
Equivalent to \samp{len(\var{anyset})}. Raises a
\exception{PyExc_SystemError} if the argument is not a \class{set},
\class{frozenset}, or an instance of a subtype.
\bifuncindex{len}
\end{cfuncdesc}
\begin{cfuncdesc}{int}{PySet_GET_SIZE}{PyObject *anyset}
Macro form of \cfunction{PySet_Size()} without error checking.
\end{cfuncdesc}
\begin{cfuncdesc}{int}{PySet_Contains}{PyObject *anyset, PyObject *key}
Returns 1 if found, 0 if not found, and -1 if an error is
encountered. Unlike the Python \method{__contains__()} method, this
function does not automatically convert unhashable sets into temporary
frozensets. Raises a \exception{TypeError} if the key is unhashable.
\end{cfuncdesc}
\begin{cfuncdesc}{int}{PySet_Discard}{PyObject *anyset, PyObject *key}
Returns 1 if found and removed, 0 if not found (no action taken),
and -1 if an error is encountered. Does not raise \exception{KeyError}
for missing keys. Raises a \exception{TypeError} if the key is unhashable.
Unlike the Python \method{discard()} method, this function does
not automatically convert unhashable sets into temporary frozensets.
\end{cfuncdesc}
The following functions are available for instances of \class{set} or
its subtypes but not for instances of \class{frozenset} or its subtypes.
\begin{cfuncdesc}{int}{PySet_Add}{PyObject *set, PyObject *key}
Adds \var{key} to a \class{set} instance. Does not apply to
\class{frozenset} instances. Returns 0 on success or -1 on failure.
Raises a \exception{TypeError} if the key is unhashable.
Raises a \exception{MemoryError} if there is no room to grow.
Raises a \exception{SystemError} if \var{key} is an not an instance
of \class{set} or its subtype.
\end{cfuncdesc}
\begin{cfuncdesc}{PyObject*}{PySet_Pop}{PyObject *set}
Returns a new reference to an arbitrary object in the \var{set},
and removes the object from the \var{set}. Returns \NULL{} on
failure. Raises \exception{KeyError} if the set is empty.
Raises a \exception{SystemError} if \var{key} is an not an instance
of \class{set} or its subtype.
\end{cfuncdesc}

View File

@ -74,6 +74,15 @@ PyAPI_DATA(PyTypeObject) PyFrozenSet_Type;
PyType_IsSubtype((ob)->ob_type, &PySet_Type) || \
PyType_IsSubtype((ob)->ob_type, &PyFrozenSet_Type))
PyAPI_FUNC(PyObject *) PySet_New(PyObject *);
PyAPI_FUNC(PyObject *) PyFrozenSet_New(PyObject *);
PyAPI_FUNC(int) PySet_Size(PyObject *anyset);
#define PySet_GET_SIZE(so) (((PySetObject *)(so))->used)
PyAPI_FUNC(int) PySet_Contains(PyObject *anyset, PyObject *key);
PyAPI_FUNC(int) PySet_Discard(PyObject *anyset, PyObject *key);
PyAPI_FUNC(int) PySet_Add(PyObject *set, PyObject *key);
PyAPI_FUNC(PyObject *) PySet_Pop(PyObject *set);
#ifdef __cplusplus
}
#endif

View File

@ -439,6 +439,8 @@ Build
C API
-----
- Added a C API for set and frozenset objects.
- Removed PyRange_New().

View File

@ -843,7 +843,7 @@ frozenset_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return iterable;
}
result = make_new_set(type, iterable);
if (result == NULL || set_len(result))
if (result == NULL || PySet_GET_SIZE(result))
return result;
Py_DECREF(result);
}
@ -1052,7 +1052,7 @@ set_intersection(PySetObject *so, PyObject *other)
int pos = 0;
setentry *entry;
if (set_len(other) > set_len((PyObject *)so)) {
if (PySet_GET_SIZE(other) > PySet_GET_SIZE(so)) {
tmp = (PyObject *)so;
so = (PySetObject *)other;
other = tmp;
@ -1381,7 +1381,7 @@ set_issubset(PySetObject *so, PyObject *other)
Py_DECREF(tmp);
return result;
}
if (set_len((PyObject *)so) > set_len(other))
if (PySet_GET_SIZE(so) > PySet_GET_SIZE(other))
Py_RETURN_FALSE;
while (set_next(so, &pos, &entry)) {
@ -1426,11 +1426,11 @@ set_richcompare(PySetObject *v, PyObject *w, int op)
}
switch (op) {
case Py_EQ:
if (set_len((PyObject *)v) != set_len(w))
if (PySet_GET_SIZE(v) != PySet_GET_SIZE(w))
Py_RETURN_FALSE;
return set_issubset(v, w);
case Py_NE:
if (set_len((PyObject *)v) != set_len(w))
if (PySet_GET_SIZE(v) != PySet_GET_SIZE(w))
Py_RETURN_TRUE;
r1 = set_issubset(v, w);
assert (r1 != NULL);
@ -1442,11 +1442,11 @@ set_richcompare(PySetObject *v, PyObject *w, int op)
case Py_GE:
return set_issuperset(v, w);
case Py_LT:
if (set_len((PyObject *)v) >= set_len(w))
if (PySet_GET_SIZE(v) >= PySet_GET_SIZE(w))
Py_RETURN_FALSE;
return set_issubset(v, w);
case Py_GT:
if (set_len((PyObject *)v) <= set_len(w))
if (PySet_GET_SIZE(v) <= PySet_GET_SIZE(w))
Py_RETURN_FALSE;
return set_issuperset(v, w);
}
@ -1472,7 +1472,7 @@ frozenset_hash(PyObject *self)
if (so->hash != -1)
return so->hash;
hash *= set_len(self) + 1;
hash *= PySet_GET_SIZE(self) + 1;
while (set_next(so, &pos, &entry)) {
/* Work to increase the bit dispersion for closely spaced hash
values. The is important because some use cases have many
@ -1918,3 +1918,67 @@ PyTypeObject PyFrozenSet_Type = {
frozenset_new, /* tp_new */
PyObject_GC_Del, /* tp_free */
};
/***** C API functions *************************************************/
PyObject *
PySet_New(PyObject *iterable)
{
return make_new_set(&PySet_Type, iterable);
}
PyObject *
PyFrozenSet_New(PyObject *iterable)
{
PyObject *args = NULL, *result;
if (iterable != NULL) {
args = PyTuple_Pack(1, iterable);
if (args == NULL)
return NULL;
}
result = frozenset_new(&PyFrozenSet_Type, args, NULL);
Py_DECREF(args);
return result;
}
int
PySet_Contains(PyObject *anyset, PyObject *key)
{
if (!PyAnySet_Check(anyset)) {
PyErr_BadInternalCall();
return -1;
}
return set_contains_key((PySetObject *)anyset, key);
}
int
PySet_Discard(PyObject *anyset, PyObject *key)
{
if (!PyAnySet_Check(anyset)) {
PyErr_BadInternalCall();
return -1;
}
return set_discard_key((PySetObject *)anyset, key);
}
int
PySet_Add(PyObject *set, PyObject *key)
{
if (!PyType_IsSubtype(set->ob_type, &PySet_Type)) {
PyErr_BadInternalCall();
return -1;
}
return set_add_key((PySetObject *)set, key);
}
PyObject *
PySet_Pop(PyObject *set)
{
if (!PyType_IsSubtype(set->ob_type, &PySet_Type)) {
PyErr_BadInternalCall();
return NULL;
}
return set_pop((PySetObject *)set);
}

View File

@ -773,11 +773,9 @@ r_object(RFILE *p)
if (v == NULL)
return v;
if (type == TYPE_SET)
v3 = PyObject_CallFunctionObjArgs(
(PyObject *)&PySet_Type, v, NULL);
v3 = PySet_New(v);
else
v3 = PyObject_CallFunctionObjArgs(
(PyObject *)&PyFrozenSet_Type, v, NULL);
v3 = PyFrozenSet_New(v);
Py_DECREF(v);
return v3;