Steven D'Aprano wrote: > A re-occurring feature request is to add a default to itemgetter and > attrgetter. For example, we might say: > > from operator import itemgetter > f = itemgetter(1, 6, default="spam") # proposed feature > f("Hello World!") # returns ('e', 'W') > f("Hello") # returns ('e', 'spam') > > > Two senior developers have rejected this feature, saying that we should > just use a lambda instead. > > I might be slow today, but I cannot see how to write a clear, obvious, > efficient lambda that provides functionality equivalent to itemgetter > with a default value. > > Putting aside the case where itemgetter takes multiple indexes, how about > the single index case? How could we get that functionality using a lambda > which is simple and obvious enough to use on the fly as needed, and > reasonably efficient? > > Here are the specifications: > > * you must use lambda, not def; > > * the lambda must take a single function, the sequence you want to > extract an item from; > > * you can hard-code the index in the body of the lambda; > > * you can hard-code the default value in the body of the lambda; > > * if sequence[index] exists, return that value; > > * otherwise return the default value; > > * it should support both positive and negative indices. > > Example: given an index of 2 and a default of "spam": > > (lambda seq: ... )("abcd") returns "c" > > (lambda seq: ... )("") returns "spam" > > > I might be missing something horribly obvious, but I can't see how to do > this using a lambda. I tried using slicing: > > seq[index:index+1] > > which will return either an empty slice or a one-item slice, but that > didn't help me. I feel I'm missing something either obvious, or something > impossible, and I don't know which. > > (This isn't a code-golf problem. I care more about being able to do it at > all, than about doing it in the minimum number of characters.)
I think you have established that there is no straight-forward way to write this as a lambda. But is adding a default to itemgetter the right conclusion? If there were an exception-catching decorator you could write f = catch(IndexError, "spam")(itemgetter(2)) >>> from operator import itemgetter >>> def catch(exc, default): ... def deco(f): ... def catcher(*args, **kw): ... try: return f(*args, **kw) ... except exc: return default ... return catcher ... return deco ... >>> f = catch((IndexError, KeyError), "spam")(itemgetter(1)) >>> f("abc") 'b' >>> f("") 'spam' >>> f(dict(a=1)) 'spam' >>> f({1: "ham"}) 'ham' This may be useful for other applications: >>> g = catch(ZeroDivisionError, "#error")(lambda x: 1/x) >>> g(2) 0.5 >>> g(0) '#error' -- https://mail.python.org/mailman/listinfo/python-list