[Python-Dev] Patch to skip positional arguments with partials

Calvin Spealman ironfroggy at gmail.com
Tue Apr 24 05:51:45 CEST 2007


I posted about this on python-ideas, and didn't get any objections
about the idea itself, so I took the opportunity to dive into the C
API and get my hands dirty. I posted the idea and patch as a report on
SF (1706256). For anyone interested at least to look over, I'm also
just including the small patch at the end of this email. Everything is
passing for me with these changes, except the inability to subclass
the partial type. I've found the current end of my abilities in
understanding how I broke that, so I'd appreciate any reaction to the
situation.

Index: Modules/_functoolsmodule.c
===================================================================
--- Modules/_functoolsmodule.c	(revision 54922)
+++ Modules/_functoolsmodule.c	(working copy)
@@ -21,6 +21,7 @@
 } partialobject;

 static PyTypeObject partial_type;
+static PyObject *partial_skip;

 static PyObject *
 partial_new(PyTypeObject *type, PyObject *args, PyObject *kw)
@@ -89,6 +90,10 @@
 {
 	PyObject *ret;
 	PyObject *argappl = NULL, *kwappl = NULL;
+	PyObject *ptoargscopy, *arg;
+	Py_ssize_t skip_index = 0;
+	Py_ssize_t pull_index = 0;
+	Py_ssize_t i;

 	assert (PyCallable_Check(pto->fn));
 	assert (PyTuple_Check(pto->args));
@@ -101,7 +106,25 @@
 		argappl = pto->args;
 		Py_INCREF(pto->args);
 	} else {
-		argappl = PySequence_Concat(pto->args, args);
+		
+		// For each partial_skip in the pto args, replace it with a new arg
+		ptoargscopy = PyTuple_New(PyTuple_GET_SIZE(pto->args));
+		for (i = 0; i<PyTuple_GET_SIZE(pto->args); ++i) {
+			arg = PyTuple_GetItem(pto->args, i);
+			Py_XINCREF(arg);
+			PyTuple_SetItem(ptoargscopy, i, arg);
+			if (arg == NULL) {
+				break;
+			} else if (arg == partial_skip) {
+				arg = PyTuple_GetItem(args, pull_index);
+				Py_XINCREF(arg);
+				PyTuple_SetItem(ptoargscopy, i, arg);
+				pull_index += 1;
+			}
+		}
+
+		arg = PyTuple_GetSlice(args, pull_index, PySequence_Length(args));
+		argappl = PySequence_Concat(ptoargscopy, arg);
 		if (argappl == NULL)
 			return NULL;
 	}
@@ -142,7 +165,8 @@

 PyDoc_STRVAR(partial_doc,
 "partial(func, *args, **keywords) - new function with partial application\n\
-	of the given arguments and keywords.\n");
+	of the given arguments and keywords. Pass partial.skip to any positional\n\
+	argument to be bound to later.");

 #define OFF(x) offsetof(partialobject, x)
 static PyMemberDef partial_memberlist[] = {
@@ -255,7 +280,7 @@
 init_functools(void)
 {
 	int i;
-	PyObject *m;
+	PyObject *m, *builtins, *skip, *object;
 	char *name;
 	PyTypeObject *typelist[] = {
 		&partial_type,
@@ -274,4 +299,16 @@
 		Py_INCREF(typelist[i]);
 		PyModule_AddObject(m, name+1, (PyObject *)typelist[i]);
 	}
+
+	builtins = PyImport_ImportModule("__builtin__");
+	object = PyObject_GetAttrString(builtins, "object");
+	Py_DECREF(builtins);
+	skip = PyObject_CallObject(object, NULL, NULL);
+	Py_DECREF(object);
+
+	partial_type.tp_dict = PyDict_New();
+	PyDict_SetItemString(partial_type.tp_dict, "skip", skip);
+	partial_skip = skip;
+
+	// skip is not decref'ed because a reference is always held by the
static pointer.
 }


-- 
Read my blog! I depend on your acceptance of my opinion! I am interesting!
http://ironfroggy-code.blogspot.com/


More information about the Python-Dev mailing list