[Python-Dev] __contains__ hook
Guido van Rossum
guido@CNRI.Reston.VA.US
Wed, 02 Feb 2000 11:35:03 -0500
Moshe seems eager to get comments on this post :-)
> Here's a very preliminary, very hackish version of a hook for the "in"
> operator.
>
> Basically, use like:
>
> class spam:
> def __contains__(self, o):
> return 1
> 6 in spam() (answers 1)
>
> I must say I was horrified by the current way the operator was handled:
> very non-OO-ish. I'd much rather there'd be a slot in the sequence
> interface for this method. This is why there's still no way to use the
> hook with regular C extension types.
>
> Have fun!
>
> (BTW: I've tested it only minimally, so it might break your Python. Use
> with caution).
>
> PS.
> Eric, you can implement sets the *right* way this time.
For those who, like me, are too lazy to unpack attachments, here's the
text of Moshe's patch:
> *** ../../../python/dist/src/Objects/abstract.c Fri Oct 15 14:09:02 1999
> --- Objects/abstract.c Tue Feb 1 10:34:34 2000
> ***************
> *** 1110,1115 ****
> --- 1110,1140 ----
> }
> return 0;
> }
> + /* special case for instances. Basically emulating Python code,
> + but optimizations will come later */
> + if (PyInstance_Check(w)) {
> + PyObject *py__contains__, *py_ret, *py_args;
> + int ret;
> +
> + py__contains__ = PyObject_GetAttrString(w, "__contains__");
> + if(py__contains__ == NULL)
> + return -1;
> + py_args = PyTuple_New(1);
> + if(py_args == NULL) {
> + Py_DECREF(py__contains__);
> + return -1;
> + }
> + Py_INCREF(v);
> + PyTuple_SET_ITEM(py_args, 0, v);
> + py_ret = PyObject_CallObject(py__contains__, py_args);
> + Py_DECREF(py__contains__);
> + Py_DECREF(py_args);
> + if(py_ret == NULL)
> + return -1;
> + ret = PyObject_IsTrue(py_ret);
> + Py_DECREF(py_args);
> + return ret;
> + }
>
> sq = w->ob_type->tp_as_sequence;
> if (sq == NULL || sq->sq_item == NULL) {
I like the idea of overloading 'in' (and 'not in') with __contains__.
There are several issues with this patch though (apart from the fact
that he left out the disclaimer from
http://www.python.org/1.5/bugrelease.html :-).
First of all, it actually breaks 'in' for descendants of UserList, and
other classes that define __getitem__ but not __contains__. That's
easily fixed by clearing the error and jumping forward instead of
returning an error when the GetAttrString() call fails.
Second, it's customary to define a static object variable initialized
to NULL, which is set to the interned string object; this speeds up
the lookup a bit using PyObject_GetAttr().
Micro-nit: I want a space between 'if' and '('. It just looks better.
But the real issue is what Moshe himself already brings up: contains
should have a slot in the type struct, so extension types can also
define this.
Moshe, do you feel like doing this right?
--Guido van Rossum (home page: http://www.python.org/~guido/)