#1506171: added operator.methodcaller().
This commit is contained in:
parent
e2065c65d3
commit
ebcfd11c16
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue