#1506171: added operator.methodcaller().

This commit is contained in:
Georg Brandl 2008-02-23 23:04:35 +00:00
parent e2065c65d3
commit ebcfd11c16
4 changed files with 169 additions and 0 deletions

View File

@ -538,6 +538,17 @@ Examples::
[('orange', 1), ('banana', 2), ('apple', 3), ('pear', 5)]
.. function:: methodcaller(name[, args...])
Return a callable object that calls the method *name* on its operand. If
additional arguments and/or keyword arguments are given, they will be given
to the method as well. After ``f = methodcaller('name')``, the call ``f(b)``
returns ``b.name()``. After ``f = methodcaller('name', 'foo', bar=1)``, the
call ``f(b)`` returns ``b.name('foo', bar=1)``.
.. versionadded:: 2.6
.. _operator-map:
Mapping Operators to Functions

View File

@ -440,6 +440,24 @@ class OperatorTestCase(unittest.TestCase):
self.assertEqual(operator.itemgetter(2,10,5)(data), ('2', '10', '5'))
self.assertRaises(TypeError, operator.itemgetter(2, 'x', 5), data)
def test_methodcaller(self):
self.assertRaises(TypeError, operator.methodcaller)
class A:
def foo(self, *args, **kwds):
return args[0] + args[1]
def bar(self, f=42):
return f
a = A()
f = operator.methodcaller('foo')
self.assertRaises(IndexError, f, a)
f = operator.methodcaller('foo', 1, 2)
self.assertEquals(f(a), 3)
f = operator.methodcaller('bar')
self.assertEquals(f(a), 42)
self.assertRaises(TypeError, f, a, a)
f = operator.methodcaller('bar', f=5)
self.assertEquals(f(a), 5)
def test_inplace(self):
class C(object):
def __iadd__ (self, other): return "iadd"

View File

@ -1196,6 +1196,8 @@ Library
Extension Modules
-----------------
- Patch #1506171: added operator.methodcaller().
- Patch #1826: operator.attrgetter() now supports dotted attribute paths.
- Patch #1957: syslogmodule: Release GIL when calling syslog(3)

View File

@ -620,6 +620,139 @@ static PyTypeObject attrgetter_type = {
attrgetter_new, /* tp_new */
0, /* tp_free */
};
/* methodcaller object **********************************************************/
typedef struct {
PyObject_HEAD
PyObject *name;
PyObject *args;
PyObject *kwds;
} methodcallerobject;
static PyTypeObject methodcaller_type;
static PyObject *
methodcaller_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
methodcallerobject *mc;
PyObject *name, *newargs;
if (PyTuple_GET_SIZE(args) < 1) {
PyErr_SetString(PyExc_TypeError, "methodcaller needs at least "
"one argument, the method name");
return NULL;
}
/* create methodcallerobject structure */
mc = PyObject_GC_New(methodcallerobject, &methodcaller_type);
if (mc == NULL)
return NULL;
newargs = PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args));
if (newargs == NULL) {
Py_DECREF(mc);
return NULL;
}
mc->args = newargs;
name = PyTuple_GET_ITEM(args, 0);
Py_INCREF(name);
mc->name = name;
Py_XINCREF(kwds);
mc->kwds = kwds;
PyObject_GC_Track(mc);
return (PyObject *)mc;
}
static void
methodcaller_dealloc(methodcallerobject *mc)
{
PyObject_GC_UnTrack(mc);
Py_XDECREF(mc->name);
Py_XDECREF(mc->args);
Py_XDECREF(mc->kwds);
PyObject_GC_Del(mc);
}
static int
methodcaller_traverse(methodcallerobject *mc, visitproc visit, void *arg)
{
Py_VISIT(mc->args);
Py_VISIT(mc->kwds);
return 0;
}
static PyObject *
methodcaller_call(methodcallerobject *mc, PyObject *args, PyObject *kw)
{
PyObject *method, *obj, *result;
if (!PyArg_UnpackTuple(args, "methodcaller", 1, 1, &obj))
return NULL;
method = PyObject_GetAttr(obj, mc->name);
if (method == NULL)
return NULL;
result = PyObject_Call(method, mc->args, mc->kwds);
Py_DECREF(method);
return result;
}
PyDoc_STRVAR(methodcaller_doc,
"methodcaller(name, ...) --> methodcaller object\n\
\n\
Return a callable object that calls the given method on its operand.\n\
After, f = methodcaller('name'), the call f(r) returns r.name().\n\
After, g = methodcaller('name', 'date', foo=1), the call g(r) returns\n\
r.name('date', foo=1).");
static PyTypeObject methodcaller_type = {
PyVarObject_HEAD_INIT(NULL, 0)
"operator.methodcaller", /* tp_name */
sizeof(methodcallerobject), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
(destructor)methodcaller_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 */
(ternaryfunc)methodcaller_call, /* tp_call */
0, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
methodcaller_doc, /* tp_doc */
(traverseproc)methodcaller_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 */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
methodcaller_new, /* tp_new */
0, /* tp_free */
};
/* Initialization function for the module (*must* be called initoperator) */
PyMODINIT_FUNC
@ -642,4 +775,9 @@ initoperator(void)
return;
Py_INCREF(&attrgetter_type);
PyModule_AddObject(m, "attrgetter", (PyObject *)&attrgetter_type);
if (PyType_Ready(&methodcaller_type) < 0)
return;
Py_INCREF(&methodcaller_type);
PyModule_AddObject(m, "methodcaller", (PyObject *)&methodcaller_type);
}