Defining *class* methods on C code

Michael Hudson mwh at python.net
Fri Feb 6 10:47:12 EST 2004


Thomas Heller <theller at python.net> writes:

> Michael Hudson <mwh at python.net> writes:
> 
> > Thomas Heller <theller at python.net> writes:
> >
> >> I once knew how to do it, but I cannot find or remember it anymore:
> >> 
> >> How can I attach *class* methods to a type in C code, when I have a
> >> function
> >> 
> >> PyObject *func(PyObject *type, PyObject *arg);
> >> 
> >> with the METH_O calling convention?  The type is already created, I
> >> cannot insert it into the tp_methods array anymore.
> >> 
> >> Something like this:
> >> 
> >> class X(object):
> >>     pass
> >> 
> >> def func(cls, arg):
> >>     ....
> >> 
> >> X.func = classmethod(func)
> >
> > Just stuff it into tp_dict?  You'll need to make the method object
> > yourself, but that's easy.
> >
> > I think you probably have to hope that the name of the class method
> > isn't a special method name (you'd want to call
> > typeobject.c:update_slots then, but I don't think you can arrange for
> > that to happen from outside typeobject.c as all the juicy symbols are
> > static).
> 
> No special names.  Here's the code:
> 
> static PyObject *my_method(PyObject *self, PyObject *arg)
> {
> 	Py_INCREF(arg);
> 	return arg;
> }
> 
> static PyMethodDef my_methods[] = {
> 	{ "my_method", my_method, METH_O },
> 	{ NULL, NULL },
> };
> 
> and then ('type' is the type where I want to create the class method on):
> 
> 	if (somecondition) {
> 		PyObject *func;
> 		PyObject *meth;
> 		PyMethodDef *ml = my_methods;
> 
> 		for (; ml->ml_name; ++ml) {
> 			func = PyCFunction_New(ml, NULL);
> 			if (!func)
> 				return NULL;
> 			meth = PyClassMethod_New(func);

How about "meth = PyDescr_NewClassMethod(result, ml);" here?

> 			if (!meth)
> 				return NULL;
> 			if (-1 == PyDict_SetItemString(result->tp_dict,
> 						       ml->ml_name,
> 						       meth))
> 				return NULL;
> 		}
> 	}
> 
> I know that there are refcount leaks in this snippet, but that's not the
> point.
> 
> Trying out the method:
> 
> c:\sf\ctypes>py23 -c "from ctypes import *; print c_int.my_method()"
> <class 'ctypes.c_int'>
> 
> c:\sf\ctypes>py23 -c "from ctypes import *; print c_int.my_method(0)"
> Traceback (most recent call last):
>   File "<string>", line 1, in ?
> TypeError: my_method() takes exactly one argument (2 given)
> c:\sf\ctypes>
> 
> Works *nearly*, but I would have expected that the method accepts one
> parameter, pass it as 'arg' to the C function, plus the type itself as
> 'self'.

Oh... I wouldn't have :-)

> > Or just stick TP_HEAPTYPE into tp_flags, use PyObject_SetAttr and take
> > TP_HEAPTYPE out again (that's very sick, though).
> 
> Um, why that?

Well, if "my_method" was spelt "__add__", say, you'd want to call
typeobject.c:update_slots() to get the function into the
tp_as_number.nb_add fields of the type and all the subclasses.  The
hack I outline is one way of doing that.

Cheers,
mwh

-- 
  Every now and then, Google doesn't throw up what I need so I start
  checking Altavista, Yahoo, etc.  In almost every single case, I am
  brutally reminded why I use Google in the first place.
                                                  -- John Riddoch, asr



More information about the Python-list mailing list