I’m a big fan of itemgetter and attrgetter, but sometimes when using large lists of dicts or objects, a particular key or attribute might not be present in one of the list members. I wish I could define default values to use in the event of a missing key or attribute, something like this (with similar for attrgetter):
class itemgetter:
"""
Return a callable object that fetches the given item(s) from its operand.
After f = itemgetter(2), the call f(r) returns r[2].
After g = itemgetter(2, 5, 3), the call g(r) returns (r[2], r[5], r[3])
Accepts optional keyword arguments:
default: Default value if the single item is not found in the input mapping.
defaults: Sequence of default values if corresponding item is not found in the input mapping.
"""
__slots__ = ('_items', '_call', '_defaults')
NOT_GIVEN = object()
def __init__(self, item, /, *items, default=NOT_GIVEN, defaults=NOT_GIVEN):
if not items:
self._items = (item,)
default = defaults[0] if defaults is not self.NOT_GIVEN else default
self._defaults = (default,)
if default is self.NOT_GIVEN:
def func(obj):
return obj[item]
else:
def func(obj):
return obj.get(item, default)
self._call = func
else:
self._items = items = (item,) + items
self._defaults = defaults = defaults if defaults is not self.NOT_GIVEN else (default,)
if defaults is self.NOT_GIVEN:
def func(obj):
return tuple(obj[i] for i in items)
else:
def func(obj):
return tuple(obj.get(i, d) for i, d in zip(items, defaults))
self._call = func
def __call__(self, obj, /):
return self._call(obj)
def __repr__(self):
if self._defaults is self.NOT_GIVEN:
return '%s.%s(%s)' % (self.__class__.__module__,
self.__class__.__name__,
', '.join(map(repr, self._items)))
else:
return '%s.%s(%s, defaults=(%s,))' % (self.__class__.__module__,
self.__class__.__name__,
', '.join(map(repr, self._items)),
', '.join(map(repr, self._defaults)))
def __reduce__(self):
return self.__class__, self._items, self._defaults
if __name__ == '__main__':
dd = {'a': 1, 'b': 2, 'c': 3}
ig = itemgetter("a", "b", "c", defaults=(0, 0, 0))
print(repr(ig))
print(ig(dd))
dd.pop("b")
print(ig(dd))
ig = itemgetter("a", default=0)
print(repr(ig))
print(ig(dd))
dd.pop("a")
print(ig(dd))
ig = itemgetter("a", defaults=(0,))
print(repr(ig))
print(ig(dd))
ig = itemgetter("a", "b", "c", default=0)
print(repr(ig))
print(ig(dd))
If I polished this up (and also implemented the code in the corresponding _operator C module), would this be supported for inclusion in the stdlib operator module?
– Paul