Index: Doc/library/operator.rst =================================================================== --- Doc/library/operator.rst (revision 66888) +++ Doc/library/operator.rst (working copy) @@ -507,6 +507,9 @@ ``f = attrgetter('name', 'date')``, the call ``f(b)`` returns ``(b.name, b.date)``. + ``attrgetter`` can have a ``default`` keyword, it will then return the + default when the attribute is not found. + The attribute names can also contain dots; after ``f = attrgetter('date.month')``, the call ``f(b)`` returns ``b.date.month``. @@ -525,26 +528,41 @@ operand's :meth:`__getitem__` method. If multiple items are specified, returns a tuple of lookup values. Equivalent to:: - def itemgetter(*items): + def itemgetter(*items, **kw): if len(items) == 1: item = items[0] def g(obj): - return obj[item] + try: + return obj[item] else: def g(obj): return tuple(obj[item] for item in items) - return g + def f(obj): + try: + return g(obj) + except IndexError: + if "default" in kw: + return kw["default"] + else: + raise + return f The items can be any type accepted by the operand's :meth:`__getitem__` method. Dictionaries accept any hashable value. Lists, tuples, and strings accept an index or a slice: + ``itemgetter`` can have a ``default`` keyword, it will then return the + default when the attribute is not found. + + >>> itemgetter(1)('ABCDEFG') 'B' >>> itemgetter(1,3,5)('ABCDEFG') ('B', 'D', 'F') >>> itemgetter(slice(2,None))('ABCDEFG') 'CDEFG' + >>> itemgetter(1, default="a")([]) + 'a' .. versionadded:: 2.4 Index: Lib/test/test_operator.py =================================================================== --- Lib/test/test_operator.py (revision 66888) +++ Lib/test/test_operator.py (working copy) @@ -407,6 +407,10 @@ f = operator.attrgetter('name', 'child.name', 'child.child.name') self.assertEqual(f(a), ('arthur', 'thomas', 'johnson')) + # default value + f = operator.attrgetter("a", default=2) + self.assertEqual(f(object()), 2) + def test_itemgetter(self): a = 'ABCDE' f = operator.itemgetter(2) @@ -441,6 +445,11 @@ self.assertEqual(operator.itemgetter(2,10,5)(data), ('2', '10', '5')) self.assertRaises(TypeError, operator.itemgetter(2, 'x', 5), data) + # default value + f = operator.itemgetter("a", default=2) + self.assertEquals(f([]), 2) + + def test_methodcaller(self): self.assertRaises(TypeError, operator.methodcaller) class A: Index: Modules/operator.c =================================================================== --- Modules/operator.c (revision 66888) +++ Modules/operator.c (working copy) @@ -309,6 +309,7 @@ PyObject_HEAD Py_ssize_t nitems; PyObject *item; + PyObject *defval; } itemgetterobject; static PyTypeObject itemgetter_type; @@ -320,9 +321,6 @@ PyObject *item; Py_ssize_t nitems; - if (!_PyArg_NoKeywords("itemgetter()", kwds)) - return NULL; - nitems = PyTuple_GET_SIZE(args); if (nitems <= 1) { if (!PyArg_UnpackTuple(args, "itemgetter", 1, 1, &item)) @@ -339,6 +337,16 @@ ig->item = item; ig->nitems = nitems; + if (kwds == NULL) { + ig->defval = NULL; + } + else { + ig->defval = PyDict_GetItemString(kwds, "default"); + if (ig->defval != NULL) { + Py_INCREF(ig->defval); + } + } + PyObject_GC_Track(ig); return (PyObject *)ig; } @@ -348,6 +356,7 @@ { PyObject_GC_UnTrack(ig); Py_XDECREF(ig->item); + Py_XDECREF(ig->defval); PyObject_GC_Del(ig); } @@ -366,8 +375,21 @@ if (!PyArg_UnpackTuple(args, "itemgetter", 1, 1, &obj)) return NULL; - if (nitems == 1) - return PyObject_GetItem(obj, ig->item); + if (nitems == 1) { + result = PyObject_GetItem(obj, ig->item); + if (result == NULL) { + if (ig->defval) { + Py_INCREF(ig->defval); + return ig->defval; + } + else { + return NULL; + } + } + else { + return result; + } + } assert(PyTuple_Check(ig->item)); assert(PyTuple_GET_SIZE(ig->item) == nitems); @@ -382,7 +404,13 @@ val = PyObject_GetItem(obj, item); if (val == NULL) { Py_DECREF(result); - return NULL; + if (NULL != ig->defval) { + Py_INCREF(ig->defval); + return ig->defval; + } + else { + return NULL; + } } PyTuple_SET_ITEM(result, i, val); } @@ -394,7 +422,9 @@ \n\ Return a callable object that fetches the given item(s) from its operand.\n\ After, f=itemgetter(2), the call f(r) returns r[2].\n\ -After, g=itemgetter(2,5,3), the call g(r) returns (r[2], r[5], r[3])"); +After, g=itemgetter(2,5,3), the call g(r) returns (r[2], r[5], r[3])\n\n\ +itemgetter can have a 'default' keyword, which will be return when the item\n\ +was not found"); static PyTypeObject itemgetter_type = { PyVarObject_HEAD_INIT(NULL, 0) @@ -446,6 +476,7 @@ PyObject_HEAD Py_ssize_t nattrs; PyObject *attr; + PyObject *defval; } attrgetterobject; static PyTypeObject attrgetter_type; @@ -457,9 +488,6 @@ PyObject *attr; Py_ssize_t nattrs; - if (!_PyArg_NoKeywords("attrgetter()", kwds)) - return NULL; - nattrs = PyTuple_GET_SIZE(args); if (nattrs <= 1) { if (!PyArg_UnpackTuple(args, "attrgetter", 1, 1, &attr)) @@ -475,6 +503,15 @@ Py_INCREF(attr); ag->attr = attr; ag->nattrs = nattrs; + if (kwds == NULL) { + ag->defval = NULL; + } + else { + ag->defval = PyDict_GetItemString(kwds, "default"); + if (ag->defval == NULL) { + Py_INCREF(ag->defval); + } + } PyObject_GC_Track(ag); return (PyObject *)ag; @@ -485,6 +522,7 @@ { PyObject_GC_UnTrack(ag); Py_XDECREF(ag->attr); + Py_XDECREF(ag->defval); PyObject_GC_Del(ag); } @@ -529,7 +567,8 @@ Py_DECREF(str); Py_DECREF(obj); if (newobj == NULL) - return NULL; + return NULL; + obj = newobj; if (p == NULL) break; s = p+1; @@ -546,8 +585,21 @@ if (!PyArg_UnpackTuple(args, "attrgetter", 1, 1, &obj)) return NULL; - if (ag->nattrs == 1) - return dotted_getattr(obj, ag->attr); + if (ag->nattrs == 1) { + result = dotted_getattr(obj, ag->attr); + if (result == NULL) { + if (ag->defval) { + Py_INCREF(ag->defval); + return ag->defval; + } + else { + return NULL; + } + } + else { + return result; + } + } assert(PyTuple_Check(ag->attr)); assert(PyTuple_GET_SIZE(ag->attr) == nattrs); @@ -562,7 +614,13 @@ val = dotted_getattr(obj, attr); if (val == NULL) { Py_DECREF(result); - return NULL; + if (ag->defval) { + Py_INCREF(ag->defval); + return ag->defval; + } + else { + return NULL; + } } PyTuple_SET_ITEM(result, i, val); } @@ -576,7 +634,9 @@ After, f=attrgetter('name'), the call f(r) returns r.name.\n\ After, g=attrgetter('name', 'date'), the call g(r) returns (r.name, r.date).\n\ After, h=attrgetter('name.first', 'name.last'), the call h(r) returns\n\ -(r.name.first, r.name.last)."); +(r.name.first, r.name.last).\n\n\ +attrgetter can have a 'default' keyword, which will be return when the item\n\ +was not found"); static PyTypeObject attrgetter_type = { PyVarObject_HEAD_INIT(NULL, 0)