[Python-3000] Method descriptors
Marcin ‘Qrczak’ Kowalczyk
qrczak at knm.org.pl
Tue Dec 11 10:50:59 CET 2007
Dnia 11-12-2007, Wt o godzinie 01:32 +0100, Christian Heimes pisze:
> Marcin, can you come up with a patch?
Below is what I have, after some cleaning and renaming. Needs more
cleaning to plug it into Python modules and conform to Python coding
standards.
I'm not convinced that Python on its own needs it. Perhaps Python
already distinguishes function-like objects from others rigorously
enough that they should care about being descriptors themselves.
CFunction does not define descr_get - why? Maybe it should?
I needed it for my language binding because Kogut objects are wrapped
in Python in a generic way, they have the same Python type no matter
whether they were functions or not on the other side. I needed separate
Python types only for exceptions being thrown, for iterators being run,
and for explicit wrapping of functions to suppress default conversions
of the arguments and the result.
Now that I am thinking about it, the generic Kogut object wrapper should
emulate the Python behavior more closely in this respect and provide a
descr_get which checks whether the wrapped object has a Kogut FUNCTION
type, like I'm emulating various other protocols (numbers, collections,
pickling, basic I/O) which also need to sometimes do different things
depending on the type of the object on the other side. In particular
the issue of iteration over dictionaries, coupled with the inability to
reliably distinguish a Python dictionary from a sequence was a headache.
Successfully solved.
Having descr_get in the basic object wrapper will make the method wrapper
unnecessary. I will remove it today.
Or maybe the language binding should generate new Python types on
the fly, to correspond 1-1 to Kogut types, and similarly in the other
direction. This would complicate things and introduce some overhead,
but perhaps this how it should really be done.
I never liked the idea that in Python an instance method is distinguished
from a class member by the fact of being a function. What if someone
wants to have a function as a class member? Now Python has staticmethod
to override this behavior (the method wrapper that we will add or not is
the overriding in the other direction), but it would be more clean if
Python distinguished functions from methods since the beginning, e.g.:
method self.foo(args):
...
with regular functions not being turned into methods, i.e. being
staticmethods by default.
typedef struct {
PyObject_HEAD
PyObject *fun;
} PyInstanceMethod;
static void
PyInstanceMethod_dealloc(PyObject *self) {
_PyObject_GC_UNTRACK(self);
Py_DECREF(((PyInstanceMethod *)self)->fun);
PyObject_GC_Del(self);
}
static int
PyInstanceMethod_traverse(PyObject *self, visitproc visit, void *arg) {
Py_VISIT(((PyInstanceMethod *)self)->fun);
return 0;
}
static PyObject *
PyInstanceMethod_descr_get(PyObject *descr, PyObject *obj, PyObject *type) {
PyObject *fun = ((PyInstanceMethod *)descr)->fun;
(void)type;
if (obj == NULL)
return fun;
else
return PyMethod_New(fun, obj);
}
PyTypeObject PyInstanceMethod_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
\"instance-method\", /* tp_name */
sizeof(PyInstanceMethod), /* tp_basicsize */
0, /* tp_itemsize */
PyInstanceMethod_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT
| Py_TPFLAGS_HAVE_GC, /* tp_flags */
0, /* tp_doc */
PyInstanceMethod_traverse, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
PyInstanceMethod_descr_get, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
PyType_GenericNew, /* tp_new */
0, /* tp_free */
0, /* tp_is_gc */
0, /* tp_bases */
0, /* tp_mro */
0, /* tp_cache */
0, /* tp_subclasses */
0, /* tp_weaklist */
0, /* tp_del */
};
PyObject *
PyInstanceMethod_New(PyObject *fun) {
PyInstanceMethod *method;
method = PyObject_GC_New(PyInstanceMethod, &PyInstanceMethod_Type);
if (method == NULL) return NULL;
Py_INCREF(fun);
method->fun = fun;
_PyObject_GC_TRACK(method);
return (PyObject *)method;
}
Initialization:
if (PyType_Ready(&PyInstanceMethod_Type) == -1) ...
Needs the ability to be constructed from Python.
--
__("< Marcin Kowalczyk
\__/ qrczak at knm.org.pl
^^ http://qrnik.knm.org.pl/~qrczak/
More information about the Python-3000
mailing list