2003-11-17 00:17:49 +08:00
|
|
|
#include "Python.h"
|
|
|
|
|
|
|
|
/* set object implementation
|
|
|
|
written and maintained by Raymond D. Hettinger <python@rcn.com>
|
|
|
|
derived from sets.py written by Greg V. Wilson, Alex Martelli,
|
|
|
|
Guido van Rossum, Raymond Hettinger, and Tim Peters.
|
|
|
|
|
|
|
|
Copyright (c) 2003 Python Software Foundation.
|
|
|
|
All rights reserved.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Fast access macros */
|
|
|
|
|
|
|
|
#define DICT_CONTAINS(d, k) (d->ob_type->tp_as_sequence->sq_contains(d, k))
|
|
|
|
#define IS_SET(so) (so->ob_type == &PySet_Type || so->ob_type == &PyFrozenSet_Type)
|
|
|
|
|
|
|
|
/* set object **********************************************************/
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
make_new_set(PyTypeObject *type, PyObject *iterable)
|
|
|
|
{
|
|
|
|
PyObject *data;
|
|
|
|
PyObject *it = NULL;
|
|
|
|
PyObject *item;
|
|
|
|
PySetObject *so;
|
|
|
|
|
|
|
|
/* Get iterator. */
|
|
|
|
if (iterable != NULL) {
|
|
|
|
it = PyObject_GetIter(iterable);
|
|
|
|
if (it == NULL)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
data = PyDict_New();
|
|
|
|
if (data == NULL) {
|
|
|
|
Py_DECREF(it);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (it != NULL && (item = PyIter_Next(it)) != NULL) {
|
|
|
|
if (PyDict_SetItem(data, item, Py_True) == -1) {
|
|
|
|
Py_DECREF(it);
|
|
|
|
Py_DECREF(data);
|
|
|
|
Py_DECREF(item);
|
|
|
|
PyErr_SetString(PyExc_TypeError,
|
|
|
|
"all set entries must be immutable");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
Py_DECREF(item);
|
|
|
|
}
|
|
|
|
Py_XDECREF(it);
|
|
|
|
if (PyErr_Occurred()) {
|
|
|
|
Py_DECREF(data);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* create PySetObject structure */
|
|
|
|
so = (PySetObject *)type->tp_alloc(type, 0);
|
|
|
|
if (so == NULL) {
|
|
|
|
Py_DECREF(data);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
so->data = data;
|
|
|
|
so->hash = -1;
|
|
|
|
|
|
|
|
return (PyObject *)so;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
set_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
|
|
|
{
|
|
|
|
PyObject *iterable = NULL;
|
|
|
|
|
|
|
|
if (!PyArg_UnpackTuple(args, type->tp_name, 0, 1, &iterable))
|
|
|
|
return NULL;
|
|
|
|
return make_new_set(type, iterable);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
set_dealloc(PySetObject *so)
|
|
|
|
{
|
|
|
|
PyObject_GC_UnTrack(so);
|
|
|
|
Py_XDECREF(so->data);
|
|
|
|
so->ob_type->tp_free(so);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
set_traverse(PySetObject *so, visitproc visit, void *arg)
|
|
|
|
{
|
|
|
|
if (so->data)
|
|
|
|
return visit(so->data, arg);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
set_iter(PySetObject *so)
|
|
|
|
{
|
|
|
|
return PyObject_GetIter(so->data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
set_len(PySetObject *so)
|
|
|
|
{
|
|
|
|
return PyDict_Size(so->data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
set_contains(PySetObject *so, PyObject *key)
|
|
|
|
{
|
|
|
|
return DICT_CONTAINS(so->data, key);
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
set_copy(PySetObject *so)
|
|
|
|
{
|
|
|
|
PyObject *data;
|
|
|
|
PySetObject *newso;
|
|
|
|
|
|
|
|
data = PyDict_Copy(so->data);
|
|
|
|
if (data == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
newso = (PySetObject *)(so->ob_type->tp_alloc(so->ob_type, 0));
|
|
|
|
if (newso == NULL) {
|
|
|
|
Py_DECREF(data);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
newso->data = data;
|
|
|
|
newso->hash = so->hash;
|
|
|
|
return (PyObject *)newso;
|
|
|
|
}
|
|
|
|
|
|
|
|
PyDoc_STRVAR(copy_doc, "Return a shallow copy of a set.");
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
set_union(PySetObject *so, PyObject *other)
|
|
|
|
{
|
|
|
|
PySetObject *result;
|
|
|
|
PyObject *item, *data, *it;
|
|
|
|
|
|
|
|
result = (PySetObject *)set_copy(so);
|
|
|
|
it = PyObject_GetIter(other);
|
|
|
|
if (it == NULL) {
|
|
|
|
Py_DECREF(result);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
data = result->data;
|
|
|
|
while ((item = PyIter_Next(it)) != NULL) {
|
|
|
|
if (PyDict_SetItem(data, item, Py_True) == -1) {
|
|
|
|
Py_DECREF(it);
|
|
|
|
Py_DECREF(result);
|
|
|
|
Py_DECREF(item);
|
|
|
|
PyErr_SetString(PyExc_TypeError,
|
|
|
|
"all set entries must be immutable");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
Py_DECREF(item);
|
|
|
|
}
|
|
|
|
Py_DECREF(it);
|
|
|
|
if (PyErr_Occurred()) {
|
|
|
|
Py_DECREF(result);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return (PyObject *)result;
|
|
|
|
}
|
|
|
|
|
|
|
|
PyDoc_STRVAR(union_doc,
|
|
|
|
"Return the union of two sets as a new set.\n\
|
|
|
|
\n\
|
|
|
|
(i.e. all elements that are in either set.)");
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
set_union_update(PySetObject *so, PyObject *other)
|
|
|
|
{
|
|
|
|
PyObject *item, *data, *it;
|
|
|
|
|
|
|
|
it = PyObject_GetIter(other);
|
|
|
|
if (it == NULL)
|
|
|
|
return NULL;
|
|
|
|
data = so->data;
|
|
|
|
|
|
|
|
while ((item = PyIter_Next(it)) != NULL) {
|
|
|
|
if (PyDict_SetItem(data, item, Py_True) == -1) {
|
|
|
|
Py_DECREF(it);
|
|
|
|
Py_DECREF(item);
|
|
|
|
PyErr_SetString(PyExc_TypeError,
|
|
|
|
"all set entries must be immutable");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
Py_DECREF(item);
|
|
|
|
}
|
|
|
|
Py_DECREF(it);
|
|
|
|
if (PyErr_Occurred())
|
|
|
|
return NULL;
|
|
|
|
Py_RETURN_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
PyDoc_STRVAR(union_update_doc,
|
|
|
|
"Update a set with the union of itself and another.");
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
set_or(PySetObject *so, PyObject *other)
|
|
|
|
{
|
|
|
|
if (!IS_SET(so) || !IS_SET(other)) {
|
|
|
|
Py_INCREF(Py_NotImplemented);
|
|
|
|
return Py_NotImplemented;
|
|
|
|
}
|
|
|
|
return set_union(so, other);
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
set_ior(PySetObject *so, PyObject *other)
|
|
|
|
{
|
|
|
|
PyObject *result;
|
|
|
|
|
|
|
|
if (!IS_SET(other)) {
|
|
|
|
Py_INCREF(Py_NotImplemented);
|
|
|
|
return Py_NotImplemented;
|
|
|
|
}
|
|
|
|
result = set_union_update(so, other);
|
|
|
|
if (result == NULL)
|
|
|
|
return NULL;
|
|
|
|
Py_DECREF(result);
|
|
|
|
Py_INCREF(so);
|
|
|
|
return (PyObject *)so;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
set_intersection(PySetObject *so, PyObject *other)
|
|
|
|
{
|
|
|
|
PySetObject *result;
|
|
|
|
PyObject *item, *selfdata, *tgtdata, *it;
|
|
|
|
|
|
|
|
result = (PySetObject *)make_new_set(so->ob_type, NULL);
|
|
|
|
if (result == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
it = PyObject_GetIter(other);
|
|
|
|
if (it == NULL) {
|
|
|
|
Py_DECREF(result);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
selfdata = so->data;
|
|
|
|
tgtdata = result->data;
|
|
|
|
while ((item = PyIter_Next(it)) != NULL) {
|
|
|
|
if (DICT_CONTAINS(selfdata, item)) {
|
|
|
|
if (PyDict_SetItem(tgtdata, item, Py_True) == -1) {
|
|
|
|
Py_DECREF(it);
|
|
|
|
Py_DECREF(result);
|
|
|
|
Py_DECREF(item);
|
|
|
|
PyErr_SetString(PyExc_TypeError,
|
|
|
|
"all set entries must be immutable");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Py_DECREF(item);
|
|
|
|
}
|
|
|
|
Py_DECREF(it);
|
|
|
|
if (PyErr_Occurred()) {
|
|
|
|
Py_DECREF(result);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return (PyObject *)result;
|
|
|
|
}
|
|
|
|
|
|
|
|
PyDoc_STRVAR(intersection_doc,
|
|
|
|
"Return the intersection of two sets as a new set.\n\
|
|
|
|
\n\
|
|
|
|
(i.e. all elements that are in both sets.)");
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
set_intersection_update(PySetObject *so, PyObject *other)
|
|
|
|
{
|
|
|
|
PyObject *item, *selfdata, *it, *newdict, *tmp;
|
|
|
|
|
|
|
|
newdict = PyDict_New();
|
|
|
|
if (newdict == NULL)
|
|
|
|
return newdict;
|
|
|
|
|
|
|
|
it = PyObject_GetIter(other);
|
|
|
|
if (it == NULL) {
|
|
|
|
Py_DECREF(newdict);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
selfdata = so->data;
|
|
|
|
while ((item = PyIter_Next(it)) != NULL) {
|
|
|
|
if (DICT_CONTAINS(selfdata, item)) {
|
|
|
|
if (PyDict_SetItem(newdict, item, Py_True) == -1) {
|
|
|
|
Py_DECREF(newdict);
|
|
|
|
Py_DECREF(it);
|
|
|
|
Py_DECREF(item);
|
|
|
|
PyErr_SetString(PyExc_TypeError,
|
|
|
|
"all set entries must be immutable");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Py_DECREF(item);
|
|
|
|
}
|
|
|
|
Py_DECREF(it);
|
|
|
|
if (PyErr_Occurred()) {
|
|
|
|
Py_DECREF(newdict);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
tmp = so->data;
|
|
|
|
so->data = newdict;
|
|
|
|
Py_DECREF(tmp);
|
|
|
|
Py_RETURN_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
PyDoc_STRVAR(intersection_update_doc,
|
|
|
|
"Update a set with the intersection of itself and another.");
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
set_and(PySetObject *so, PyObject *other)
|
|
|
|
{
|
|
|
|
if (!IS_SET(so) || !IS_SET(other)) {
|
|
|
|
Py_INCREF(Py_NotImplemented);
|
|
|
|
return Py_NotImplemented;
|
|
|
|
}
|
|
|
|
return set_intersection(so, other);
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
set_iand(PySetObject *so, PyObject *other)
|
|
|
|
{
|
|
|
|
PyObject *result;
|
|
|
|
|
|
|
|
if (!IS_SET(other)) {
|
|
|
|
Py_INCREF(Py_NotImplemented);
|
|
|
|
return Py_NotImplemented;
|
|
|
|
}
|
|
|
|
result = set_intersection_update(so, other);
|
|
|
|
if (result == NULL)
|
|
|
|
return NULL;
|
|
|
|
Py_DECREF(result);
|
|
|
|
Py_INCREF(so);
|
|
|
|
return (PyObject *)so;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
set_difference(PySetObject *so, PyObject *other)
|
|
|
|
{
|
|
|
|
PySetObject *result;
|
|
|
|
PyObject *item, *tgtdata, *it;
|
|
|
|
|
|
|
|
result = (PySetObject *)set_copy(so);
|
|
|
|
if (result == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
it = PyObject_GetIter(other);
|
|
|
|
if (it == NULL) {
|
|
|
|
Py_DECREF(result);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
tgtdata = result->data;
|
|
|
|
while ((item = PyIter_Next(it)) != NULL) {
|
|
|
|
if (PyDict_DelItem(tgtdata, item) == -1) {
|
|
|
|
if (PyErr_ExceptionMatches(PyExc_KeyError))
|
|
|
|
PyErr_Clear();
|
|
|
|
else {
|
|
|
|
Py_DECREF(it);
|
|
|
|
Py_DECREF(result);
|
|
|
|
Py_DECREF(item);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Py_DECREF(item);
|
|
|
|
}
|
|
|
|
Py_DECREF(it);
|
|
|
|
if (PyErr_Occurred()) {
|
|
|
|
Py_DECREF(result);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return (PyObject *)result;
|
|
|
|
}
|
|
|
|
|
|
|
|
PyDoc_STRVAR(difference_doc,
|
|
|
|
"Return the difference of two sets as a new set.\n\
|
|
|
|
\n\
|
|
|
|
(i.e. all elements that are in this set but not the other.)");
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
set_difference_update(PySetObject *so, PyObject *other)
|
|
|
|
{
|
|
|
|
PyObject *item, *tgtdata, *it;
|
|
|
|
|
|
|
|
it = PyObject_GetIter(other);
|
|
|
|
if (it == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
tgtdata = so->data;
|
|
|
|
while ((item = PyIter_Next(it)) != NULL) {
|
|
|
|
if (PyDict_DelItem(tgtdata, item) == -1) {
|
|
|
|
if (PyErr_ExceptionMatches(PyExc_KeyError))
|
|
|
|
PyErr_Clear();
|
|
|
|
else {
|
|
|
|
Py_DECREF(it);
|
|
|
|
Py_DECREF(item);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Py_DECREF(item);
|
|
|
|
}
|
|
|
|
Py_DECREF(it);
|
|
|
|
if (PyErr_Occurred())
|
|
|
|
return NULL;
|
|
|
|
Py_RETURN_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
PyDoc_STRVAR(difference_update_doc,
|
|
|
|
"Remove all elements of another set from this set.");
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
set_sub(PySetObject *so, PyObject *other)
|
|
|
|
{
|
|
|
|
if (!IS_SET(so) || !IS_SET(other)) {
|
|
|
|
Py_INCREF(Py_NotImplemented);
|
|
|
|
return Py_NotImplemented;
|
|
|
|
}
|
|
|
|
return set_difference(so, other);
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
set_isub(PySetObject *so, PyObject *other)
|
|
|
|
{
|
|
|
|
PyObject *result;
|
|
|
|
|
|
|
|
if (!IS_SET(other)) {
|
|
|
|
Py_INCREF(Py_NotImplemented);
|
|
|
|
return Py_NotImplemented;
|
|
|
|
}
|
|
|
|
result = set_difference_update(so, other);
|
|
|
|
if (result == NULL)
|
|
|
|
return NULL;
|
|
|
|
Py_DECREF(result);
|
|
|
|
Py_INCREF(so);
|
|
|
|
return (PyObject *)so;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
set_symmetric_difference(PySetObject *so, PyObject *other)
|
|
|
|
{
|
|
|
|
PySetObject *result, *otherset;
|
|
|
|
PyObject *item, *selfdata, *otherdata, *tgtdata, *it;
|
|
|
|
|
|
|
|
selfdata = so->data;
|
|
|
|
|
|
|
|
result = (PySetObject *)set_copy(so);
|
|
|
|
if (result == NULL)
|
|
|
|
return NULL;
|
|
|
|
tgtdata = result->data;
|
|
|
|
|
|
|
|
otherset = (PySetObject *)make_new_set(so->ob_type, other);
|
|
|
|
if (otherset == NULL) {
|
|
|
|
Py_DECREF(result);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
otherdata = otherset->data;
|
|
|
|
|
|
|
|
it = PyObject_GetIter(otherdata);
|
|
|
|
if (it == NULL) {
|
|
|
|
Py_DECREF(otherset);
|
|
|
|
Py_DECREF(result);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((item = PyIter_Next(it)) != NULL) {
|
|
|
|
if (PyDict_DelItem(tgtdata, item) == -1) {
|
|
|
|
PyErr_Clear();
|
|
|
|
if (PyDict_SetItem(tgtdata, item, Py_True) == -1) {
|
|
|
|
Py_DECREF(it);
|
|
|
|
Py_DECREF(otherset);
|
|
|
|
Py_DECREF(result);
|
|
|
|
Py_DECREF(item);
|
|
|
|
PyErr_SetString(PyExc_TypeError,
|
|
|
|
"all set entries must be immutable");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Py_DECREF(item);
|
|
|
|
}
|
|
|
|
Py_DECREF(it);
|
|
|
|
Py_DECREF(otherset);
|
|
|
|
if (PyErr_Occurred()) {
|
|
|
|
Py_DECREF(result);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return (PyObject *)result;
|
|
|
|
}
|
|
|
|
|
|
|
|
PyDoc_STRVAR(symmetric_difference_doc,
|
|
|
|
"Return the symmetric difference of two sets as a new set.\n\
|
|
|
|
\n\
|
|
|
|
(i.e. all elements that are in exactly one of the sets.)");
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
set_symmetric_difference_update(PySetObject *so, PyObject *other)
|
|
|
|
{
|
|
|
|
PyObject *item, *selfdata, *it, *otherdata;
|
|
|
|
PySetObject *otherset = NULL;
|
|
|
|
|
|
|
|
selfdata = so->data;
|
|
|
|
|
|
|
|
if (PyDict_Check(other))
|
|
|
|
otherdata = other;
|
|
|
|
else if (IS_SET(other))
|
|
|
|
otherdata = ((PySetObject *)other)->data;
|
|
|
|
else {
|
|
|
|
otherset = (PySetObject *)make_new_set(so->ob_type, other);
|
|
|
|
if (otherset == NULL)
|
|
|
|
return NULL;
|
|
|
|
otherdata = otherset->data;
|
|
|
|
}
|
|
|
|
|
|
|
|
it = PyObject_GetIter(otherdata);
|
|
|
|
if (it == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
while ((item = PyIter_Next(it)) != NULL) {
|
|
|
|
if (DICT_CONTAINS(selfdata, item)) {
|
|
|
|
if (PyDict_DelItem(selfdata, item) == -1) {
|
|
|
|
Py_XDECREF(otherset);
|
|
|
|
Py_DECREF(it);
|
|
|
|
Py_DECREF(item);
|
|
|
|
PyErr_SetString(PyExc_TypeError,
|
|
|
|
"all set entries must be immutable");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (PyDict_SetItem(selfdata, item, Py_True) == -1) {
|
|
|
|
Py_XDECREF(otherset);
|
|
|
|
Py_DECREF(it);
|
|
|
|
Py_DECREF(item);
|
|
|
|
PyErr_SetString(PyExc_TypeError,
|
|
|
|
"all set entries must be immutable");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Py_DECREF(item);
|
|
|
|
}
|
|
|
|
Py_XDECREF(otherset);
|
|
|
|
Py_DECREF(it);
|
|
|
|
if (PyErr_Occurred())
|
|
|
|
return NULL;
|
|
|
|
Py_RETURN_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
PyDoc_STRVAR(symmetric_difference_update_doc,
|
|
|
|
"Update a set with the symmetric difference of itself and another.");
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
set_xor(PySetObject *so, PyObject *other)
|
|
|
|
{
|
|
|
|
if (!IS_SET(so) || !IS_SET(other)) {
|
|
|
|
Py_INCREF(Py_NotImplemented);
|
|
|
|
return Py_NotImplemented;
|
|
|
|
}
|
|
|
|
return set_symmetric_difference(so, other);
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
set_ixor(PySetObject *so, PyObject *other)
|
|
|
|
{
|
|
|
|
PyObject *result;
|
|
|
|
|
|
|
|
if (!IS_SET(other)) {
|
|
|
|
Py_INCREF(Py_NotImplemented);
|
|
|
|
return Py_NotImplemented;
|
|
|
|
}
|
|
|
|
result = set_symmetric_difference_update(so, other);
|
|
|
|
if (result == NULL)
|
|
|
|
return NULL;
|
|
|
|
Py_DECREF(result);
|
|
|
|
Py_INCREF(so);
|
|
|
|
return (PyObject *)so;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
set_issubset(PySetObject *so, PyObject *other)
|
|
|
|
{
|
|
|
|
PyObject *otherdata, *it, *item;
|
|
|
|
|
|
|
|
if (!IS_SET(other)) {
|
|
|
|
PyErr_SetString(PyExc_TypeError, "can only compare to a set");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (set_len(so) > set_len((PySetObject *)other))
|
|
|
|
Py_RETURN_FALSE;
|
|
|
|
|
|
|
|
it = PyObject_GetIter(so->data);
|
|
|
|
if (it == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
otherdata = ((PySetObject *)other)->data;
|
|
|
|
while ((item = PyIter_Next(it)) != NULL) {
|
|
|
|
if (!DICT_CONTAINS(otherdata, item)) {
|
|
|
|
Py_DECREF(it);
|
|
|
|
Py_DECREF(item);
|
|
|
|
Py_RETURN_FALSE;
|
|
|
|
}
|
|
|
|
Py_DECREF(item);
|
|
|
|
}
|
|
|
|
Py_DECREF(it);
|
|
|
|
Py_RETURN_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
PyDoc_STRVAR(issubset_doc, "Report whether another set contains this set.");
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
set_issuperset(PySetObject *so, PyObject *other)
|
|
|
|
{
|
|
|
|
if (!IS_SET(other)) {
|
|
|
|
PyErr_SetString(PyExc_TypeError, "can only compare to a set");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return set_issubset((PySetObject *)other, (PyObject *)so);
|
|
|
|
}
|
|
|
|
|
|
|
|
PyDoc_STRVAR(issuperset_doc, "Report whether this set contains another set.");
|
|
|
|
|
|
|
|
static long
|
|
|
|
set_nohash(PyObject *self)
|
|
|
|
{
|
|
|
|
PyErr_SetString(PyExc_TypeError, "set objects are unhashable");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
set_nocmp(PyObject *self)
|
|
|
|
{
|
|
|
|
PyErr_SetString(PyExc_TypeError, "cannot compare sets using cmp()");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static long
|
|
|
|
frozenset_hash(PyObject *self)
|
|
|
|
{
|
|
|
|
PyObject *it, *item;
|
|
|
|
PySetObject *so = (PySetObject *)self;
|
|
|
|
long hash = 0;
|
|
|
|
|
|
|
|
if (so->hash != -1)
|
|
|
|
return so->hash;
|
|
|
|
|
|
|
|
it = PyObject_GetIter(((PySetObject *)so)->data);
|
|
|
|
if (it == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
while ((item = PyIter_Next(it)) != NULL) {
|
|
|
|
hash ^= PyObject_Hash(item);
|
|
|
|
Py_DECREF(item);
|
|
|
|
}
|
|
|
|
so->hash = hash;
|
|
|
|
Py_DECREF(it);
|
|
|
|
return hash;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
set_richcompare(PySetObject *v, PyObject *w, int op)
|
|
|
|
{
|
|
|
|
/* XXX factor out is_set test */
|
|
|
|
if (op == Py_EQ && !IS_SET(w))
|
|
|
|
Py_RETURN_FALSE;
|
|
|
|
else if (op == Py_NE && !IS_SET(w))
|
|
|
|
Py_RETURN_TRUE;
|
|
|
|
if (!IS_SET(w)) {
|
|
|
|
PyErr_SetString(PyExc_TypeError, "can only compare to a set");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
switch (op) {
|
|
|
|
case Py_EQ:
|
|
|
|
case Py_NE:
|
|
|
|
return PyObject_RichCompare(((PySetObject *)v)->data,
|
|
|
|
((PySetObject *)w)->data, op);
|
|
|
|
case Py_LE:
|
|
|
|
return set_issubset((PySetObject *)v, w);
|
|
|
|
case Py_GE:
|
|
|
|
return set_issuperset((PySetObject *)v, w);
|
|
|
|
case Py_LT:
|
|
|
|
if (set_len(v) >= set_len((PySetObject *)w))
|
|
|
|
Py_RETURN_FALSE;
|
|
|
|
return set_issubset((PySetObject *)v, w);
|
|
|
|
case Py_GT:
|
|
|
|
if (set_len(v) <= set_len((PySetObject *)w))
|
|
|
|
Py_RETURN_FALSE;
|
|
|
|
return set_issuperset((PySetObject *)v, w);
|
|
|
|
}
|
|
|
|
Py_INCREF(Py_NotImplemented);
|
|
|
|
return Py_NotImplemented;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
set_repr(PySetObject *so)
|
|
|
|
{
|
|
|
|
PyObject *keys, *result, *listrepr;
|
|
|
|
|
|
|
|
keys = PyDict_Keys(so->data);
|
|
|
|
listrepr = PyObject_Repr(keys);
|
|
|
|
Py_DECREF(keys);
|
|
|
|
|
|
|
|
result = PyString_FromFormat("%s(%s)", so->ob_type->tp_name,
|
|
|
|
PyString_AS_STRING(listrepr));
|
|
|
|
Py_DECREF(listrepr);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
set_tp_print(PySetObject *so, FILE *fp, int flags)
|
|
|
|
{
|
|
|
|
PyObject *it, *item;
|
|
|
|
int firstpass=1;
|
|
|
|
|
|
|
|
it = PyObject_GetIter(so->data);
|
|
|
|
if (it == NULL)
|
|
|
|
return -1;
|
|
|
|
fprintf(fp, "%s([", so->ob_type->tp_name);
|
|
|
|
|
|
|
|
while ((item = PyIter_Next(it)) != NULL) {
|
|
|
|
if (firstpass == 1)
|
|
|
|
firstpass = 0;
|
|
|
|
else
|
2003-11-17 00:36:58 +08:00
|
|
|
fprintf(fp, ", ");
|
2003-11-17 00:17:49 +08:00
|
|
|
if (PyObject_Print(item, fp, 0) != 0) {
|
|
|
|
Py_DECREF(it);
|
|
|
|
Py_DECREF(item);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
Py_DECREF(item);
|
|
|
|
}
|
|
|
|
Py_DECREF(it);
|
|
|
|
fprintf(fp, "])");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
set_clear(PySetObject *so)
|
|
|
|
{
|
|
|
|
PyDict_Clear(so->data);
|
|
|
|
so->hash = -1;
|
|
|
|
Py_INCREF(Py_None);
|
|
|
|
return Py_None;
|
|
|
|
}
|
|
|
|
|
|
|
|
PyDoc_STRVAR(clear_doc, "Remove all elements from this set.");
|
|
|
|
|
|
|
|
static int
|
|
|
|
set_tp_clear(PySetObject *so)
|
|
|
|
{
|
|
|
|
PyDict_Clear(so->data);
|
|
|
|
so->hash = -1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
set_add(PySetObject *so, PyObject *item)
|
|
|
|
{
|
|
|
|
if (PyDict_SetItem(so->data, item, Py_True) == -1)
|
|
|
|
return NULL;
|
|
|
|
Py_INCREF(Py_None);
|
|
|
|
return Py_None;
|
|
|
|
}
|
|
|
|
|
|
|
|
PyDoc_STRVAR(add_doc,
|
|
|
|
"Add an element to a set.\n\
|
|
|
|
\n\
|
|
|
|
This has no effect if the element is already present.");
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
set_remove(PySetObject *so, PyObject *item)
|
|
|
|
{
|
|
|
|
if (PyDict_DelItem(so->data, item) == -1)
|
|
|
|
return NULL;
|
|
|
|
Py_INCREF(Py_None);
|
|
|
|
return Py_None;
|
|
|
|
}
|
|
|
|
|
|
|
|
PyDoc_STRVAR(remove_doc,
|
|
|
|
"Remove an element from a set; it must be a member.\n\
|
|
|
|
\n\
|
|
|
|
If the element is not a member, raise a KeyError.");
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
set_discard(PySetObject *so, PyObject *item)
|
|
|
|
{
|
|
|
|
if (PyDict_DelItem(so->data, item) == -1)
|
|
|
|
if (PyErr_ExceptionMatches(PyExc_KeyError))
|
|
|
|
PyErr_Clear();
|
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
Py_INCREF(Py_None);
|
|
|
|
return Py_None;
|
|
|
|
}
|
|
|
|
|
|
|
|
PyDoc_STRVAR(discard_doc,
|
|
|
|
"Remove an element from a set if it is a member.\n\
|
|
|
|
\n\
|
|
|
|
If the element is not a member, do nothing.");
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
set_pop(PySetObject *so)
|
|
|
|
{
|
|
|
|
PyObject *key, *value;
|
|
|
|
int pos = 0;
|
|
|
|
|
|
|
|
if (!PyDict_Next(so->data, &pos, &key, &value)) {
|
|
|
|
PyErr_SetString(PyExc_KeyError, "pop from an empty set");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
Py_INCREF(key);
|
|
|
|
if (PyDict_DelItem(so->data, key) == -1)
|
|
|
|
PyErr_Clear();
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
|
|
|
|
PyDoc_STRVAR(pop_doc, "Remove and return an arbitrary set element.");
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
set_reduce(PySetObject *so)
|
|
|
|
{
|
|
|
|
PyObject *keys=NULL, *args=NULL, *result=NULL;
|
|
|
|
|
|
|
|
keys = PyDict_Keys(so->data);
|
|
|
|
if (keys == NULL)
|
|
|
|
goto done;
|
|
|
|
args = PyTuple_Pack(1, keys);
|
|
|
|
if (args == NULL)
|
|
|
|
goto done;
|
|
|
|
result = PyTuple_Pack(2, so->ob_type, args);
|
|
|
|
done:
|
|
|
|
Py_XDECREF(args);
|
|
|
|
Py_XDECREF(keys);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
PyDoc_STRVAR(reduce_doc, "Return state information for pickling.");
|
|
|
|
|
|
|
|
static PySequenceMethods set_as_sequence = {
|
|
|
|
(inquiry)set_len, /* sq_length */
|
|
|
|
0, /* sq_concat */
|
|
|
|
0, /* sq_repeat */
|
|
|
|
0, /* sq_item */
|
|
|
|
0, /* sq_slice */
|
|
|
|
0, /* sq_ass_item */
|
|
|
|
0, /* sq_ass_slice */
|
|
|
|
(objobjproc)set_contains, /* sq_contains */
|
|
|
|
};
|
|
|
|
|
|
|
|
/* set object ********************************************************/
|
|
|
|
|
|
|
|
static PyMethodDef set_methods[] = {
|
|
|
|
{"add", (PyCFunction)set_add, METH_O,
|
|
|
|
add_doc},
|
|
|
|
{"clear", (PyCFunction)set_clear, METH_NOARGS,
|
|
|
|
clear_doc},
|
|
|
|
{"copy", (PyCFunction)set_copy, METH_NOARGS,
|
|
|
|
copy_doc},
|
|
|
|
{"__copy__", (PyCFunction)set_copy, METH_NOARGS,
|
|
|
|
copy_doc},
|
|
|
|
{"discard", (PyCFunction)set_discard, METH_O,
|
|
|
|
discard_doc},
|
|
|
|
{"difference", (PyCFunction)set_difference, METH_O,
|
|
|
|
difference_doc},
|
|
|
|
{"difference_update", (PyCFunction)set_difference_update, METH_O,
|
|
|
|
difference_update_doc},
|
|
|
|
{"intersection",(PyCFunction)set_intersection, METH_O,
|
|
|
|
intersection_doc},
|
|
|
|
{"intersection_update",(PyCFunction)set_intersection_update, METH_O,
|
|
|
|
intersection_update_doc},
|
|
|
|
{"issubset", (PyCFunction)set_issubset, METH_O,
|
|
|
|
issubset_doc},
|
|
|
|
{"issuperset", (PyCFunction)set_issuperset, METH_O,
|
|
|
|
issuperset_doc},
|
|
|
|
{"pop", (PyCFunction)set_pop, METH_NOARGS,
|
|
|
|
pop_doc},
|
|
|
|
{"__reduce__", (PyCFunction)set_reduce, METH_NOARGS,
|
|
|
|
reduce_doc},
|
|
|
|
{"remove", (PyCFunction)set_remove, METH_O,
|
|
|
|
remove_doc},
|
|
|
|
{"symmetric_difference",(PyCFunction)set_symmetric_difference, METH_O,
|
|
|
|
symmetric_difference_doc},
|
|
|
|
{"symmetric_difference_update",(PyCFunction)set_symmetric_difference_update, METH_O,
|
|
|
|
symmetric_difference_update_doc},
|
|
|
|
{"union", (PyCFunction)set_union, METH_O,
|
|
|
|
union_doc},
|
|
|
|
{"union_update",(PyCFunction)set_union_update, METH_O,
|
|
|
|
union_update_doc},
|
|
|
|
{NULL, NULL} /* sentinel */
|
|
|
|
};
|
|
|
|
|
|
|
|
static PyNumberMethods set_as_number = {
|
|
|
|
0, /*nb_add*/
|
|
|
|
(binaryfunc)set_sub, /*nb_subtract*/
|
|
|
|
0, /*nb_multiply*/
|
|
|
|
0, /*nb_divide*/
|
|
|
|
0, /*nb_remainder*/
|
|
|
|
0, /*nb_divmod*/
|
|
|
|
0, /*nb_power*/
|
|
|
|
0, /*nb_negative*/
|
|
|
|
0, /*nb_positive*/
|
|
|
|
0, /*nb_absolute*/
|
|
|
|
0, /*nb_nonzero*/
|
|
|
|
0, /*nb_invert*/
|
|
|
|
0, /*nb_lshift*/
|
|
|
|
0, /*nb_rshift*/
|
|
|
|
(binaryfunc)set_and, /*nb_and*/
|
|
|
|
(binaryfunc)set_xor, /*nb_xor*/
|
|
|
|
(binaryfunc)set_or, /*nb_or*/
|
|
|
|
0, /*nb_coerce*/
|
|
|
|
0, /*nb_int*/
|
|
|
|
0, /*nb_long*/
|
|
|
|
0, /*nb_float*/
|
|
|
|
0, /*nb_oct*/
|
|
|
|
0, /*nb_hex*/
|
|
|
|
0, /*nb_inplace_add*/
|
|
|
|
(binaryfunc)set_isub, /*nb_inplace_subtract*/
|
|
|
|
0, /*nb_inplace_multiply*/
|
|
|
|
0, /*nb_inplace_divide*/
|
|
|
|
0, /*nb_inplace_remainder*/
|
|
|
|
0, /*nb_inplace_power*/
|
|
|
|
0, /*nb_inplace_lshift*/
|
|
|
|
0, /*nb_inplace_rshift*/
|
|
|
|
(binaryfunc)set_iand, /*nb_inplace_and*/
|
|
|
|
(binaryfunc)set_ixor, /*nb_inplace_xor*/
|
|
|
|
(binaryfunc)set_ior, /*nb_inplace_or*/
|
|
|
|
};
|
|
|
|
|
|
|
|
PyDoc_STRVAR(set_doc,
|
|
|
|
"set(iterable) --> set object\n\
|
|
|
|
\n\
|
|
|
|
Build an unordered collection.");
|
|
|
|
|
|
|
|
PyTypeObject PySet_Type = {
|
|
|
|
PyObject_HEAD_INIT(&PyType_Type)
|
|
|
|
0, /* ob_size */
|
|
|
|
"set", /* tp_name */
|
|
|
|
sizeof(PySetObject), /* tp_basicsize */
|
|
|
|
0, /* tp_itemsize */
|
|
|
|
/* methods */
|
|
|
|
(destructor)set_dealloc, /* tp_dealloc */
|
|
|
|
(printfunc)set_tp_print, /* tp_print */
|
|
|
|
0, /* tp_getattr */
|
|
|
|
0, /* tp_setattr */
|
|
|
|
(cmpfunc)set_nocmp, /* tp_compare */
|
|
|
|
(reprfunc)set_repr, /* tp_repr */
|
|
|
|
&set_as_number, /* tp_as_number */
|
|
|
|
&set_as_sequence, /* tp_as_sequence */
|
|
|
|
0, /* tp_as_mapping */
|
|
|
|
set_nohash, /* tp_hash */
|
|
|
|
0, /* tp_call */
|
|
|
|
0, /* tp_str */
|
|
|
|
PyObject_GenericGetAttr, /* tp_getattro */
|
|
|
|
0, /* tp_setattro */
|
|
|
|
0, /* tp_as_buffer */
|
|
|
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_CHECKTYPES |
|
|
|
|
Py_TPFLAGS_BASETYPE, /* tp_flags */
|
|
|
|
set_doc, /* tp_doc */
|
|
|
|
(traverseproc)set_traverse, /* tp_traverse */
|
|
|
|
(inquiry)set_tp_clear, /* tp_clear */
|
|
|
|
(richcmpfunc)set_richcompare, /* tp_richcompare */
|
|
|
|
0, /* tp_weaklistoffset */
|
|
|
|
(getiterfunc)set_iter, /* tp_iter */
|
|
|
|
0, /* tp_iternext */
|
|
|
|
set_methods, /* tp_methods */
|
|
|
|
0, /* tp_members */
|
|
|
|
0, /* tp_getset */
|
|
|
|
0, /* tp_base */
|
|
|
|
0, /* tp_dict */
|
|
|
|
0, /* tp_descr_get */
|
|
|
|
0, /* tp_descr_set */
|
|
|
|
0, /* tp_dictoffset */
|
|
|
|
0, /* tp_init */
|
|
|
|
PyType_GenericAlloc, /* tp_alloc */
|
|
|
|
set_new, /* tp_new */
|
|
|
|
PyObject_GC_Del, /* tp_free */
|
|
|
|
};
|
|
|
|
|
|
|
|
/* frozenset object ********************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
static PyMethodDef frozenset_methods[] = {
|
|
|
|
{"copy", (PyCFunction)set_copy, METH_NOARGS,
|
|
|
|
copy_doc},
|
|
|
|
{"__copy__", (PyCFunction)set_copy, METH_NOARGS,
|
|
|
|
copy_doc},
|
|
|
|
{"difference",(PyCFunction)set_difference, METH_O,
|
|
|
|
difference_doc},
|
|
|
|
{"intersection",(PyCFunction)set_intersection, METH_O,
|
|
|
|
intersection_doc},
|
|
|
|
{"issubset",(PyCFunction)set_issubset, METH_O,
|
|
|
|
issubset_doc},
|
|
|
|
{"issuperset",(PyCFunction)set_issuperset, METH_O,
|
|
|
|
issuperset_doc},
|
|
|
|
{"__reduce__", (PyCFunction)set_reduce, METH_NOARGS,
|
|
|
|
reduce_doc},
|
|
|
|
{"symmetric_difference",(PyCFunction)set_symmetric_difference, METH_O,
|
|
|
|
symmetric_difference_doc},
|
|
|
|
{"union", (PyCFunction)set_union, METH_O,
|
|
|
|
union_doc},
|
|
|
|
{NULL, NULL} /* sentinel */
|
|
|
|
};
|
|
|
|
|
|
|
|
static PyNumberMethods frozenset_as_number = {
|
|
|
|
0, /*nb_add*/
|
|
|
|
(binaryfunc)set_sub, /*nb_subtract*/
|
|
|
|
0, /*nb_multiply*/
|
|
|
|
0, /*nb_divide*/
|
|
|
|
0, /*nb_remainder*/
|
|
|
|
0, /*nb_divmod*/
|
|
|
|
0, /*nb_power*/
|
|
|
|
0, /*nb_negative*/
|
|
|
|
0, /*nb_positive*/
|
|
|
|
0, /*nb_absolute*/
|
|
|
|
0, /*nb_nonzero*/
|
|
|
|
0, /*nb_invert*/
|
|
|
|
0, /*nb_lshift*/
|
|
|
|
0, /*nb_rshift*/
|
|
|
|
(binaryfunc)set_and, /*nb_and*/
|
|
|
|
(binaryfunc)set_xor, /*nb_xor*/
|
|
|
|
(binaryfunc)set_or, /*nb_or*/
|
|
|
|
};
|
|
|
|
|
|
|
|
PyDoc_STRVAR(frozenset_doc,
|
|
|
|
"frozenset(iterable) --> frozenset object\n\
|
|
|
|
\n\
|
|
|
|
Build an immutable unordered collection.");
|
|
|
|
|
|
|
|
PyTypeObject PyFrozenSet_Type = {
|
|
|
|
PyObject_HEAD_INIT(&PyType_Type)
|
|
|
|
0, /* ob_size */
|
|
|
|
"frozenset", /* tp_name */
|
|
|
|
sizeof(PySetObject), /* tp_basicsize */
|
|
|
|
0, /* tp_itemsize */
|
|
|
|
/* methods */
|
|
|
|
(destructor)set_dealloc, /* tp_dealloc */
|
|
|
|
(printfunc)set_tp_print, /* tp_print */
|
|
|
|
0, /* tp_getattr */
|
|
|
|
0, /* tp_setattr */
|
|
|
|
(cmpfunc)set_nocmp, /* tp_compare */
|
|
|
|
(reprfunc)set_repr, /* tp_repr */
|
|
|
|
&frozenset_as_number, /* tp_as_number */
|
|
|
|
&set_as_sequence, /* tp_as_sequence */
|
|
|
|
0, /* tp_as_mapping */
|
|
|
|
frozenset_hash, /* tp_hash */
|
|
|
|
0, /* tp_call */
|
|
|
|
0, /* tp_str */
|
|
|
|
PyObject_GenericGetAttr, /* tp_getattro */
|
|
|
|
0, /* tp_setattro */
|
|
|
|
0, /* tp_as_buffer */
|
|
|
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_CHECKTYPES |
|
|
|
|
Py_TPFLAGS_BASETYPE, /* tp_flags */
|
|
|
|
frozenset_doc, /* tp_doc */
|
|
|
|
(traverseproc)set_traverse, /* tp_traverse */
|
|
|
|
0, /* tp_clear */
|
|
|
|
(richcmpfunc)set_richcompare, /* tp_richcompare */
|
|
|
|
0, /* tp_weaklistoffset */
|
|
|
|
(getiterfunc)set_iter, /* tp_iter */
|
|
|
|
0, /* tp_iternext */
|
|
|
|
frozenset_methods, /* tp_methods */
|
|
|
|
0, /* tp_members */
|
|
|
|
0, /* tp_getset */
|
|
|
|
0, /* tp_base */
|
|
|
|
0, /* tp_dict */
|
|
|
|
0, /* tp_descr_get */
|
|
|
|
0, /* tp_descr_set */
|
|
|
|
0, /* tp_dictoffset */
|
|
|
|
0, /* tp_init */
|
|
|
|
PyType_GenericAlloc, /* tp_alloc */
|
|
|
|
set_new, /* tp_new */
|
|
|
|
PyObject_GC_Del, /* tp_free */
|
|
|
|
};
|