{
  "$type": "site.standard.document",
  "canonicalUrl": "https://rednafi.com/python/operators-itemgetter/",
  "description": "Use operator.itemgetter for faster sorting and element access with graceful KeyError handling via methodcaller for safer operations.",
  "path": "/python/operators-itemgetter/",
  "publishedAt": "2022-06-16T00:00:00.000Z",
  "site": "at://did:plc:fgtm2c26vfcj74rfmeggbyqj/site.standard.publication/3mnl6f7ob462z",
  "tags": [
    "Python"
  ],
  "textContent": "Python's operator.itemgetter is quite versatile. It works on pretty much any iterables and\nmap-like objects and allows you to fetch elements from them. The following snippet shows how\nyou can use it to sort a list of tuples by the first element of the tuple:\n\nHere, the itemgetter callable is doing the work of selecting the first element of every\ntuple inside the list and then the sorted function is using those values to sort the\nelements. Also, this is faster than using a lambda function and passing that to the key\nparameter to do the sorting:\n\nYou can also use itemgetter to extract multiple values from a dictionary in a single pass.\nConsider this example:\n\nSo, instead of extracting the key-value pairs with d['foo'], d['bar'], ..., itemgetter\nallows us to make it DRY. The source code of the callable is freakishly simple. Here's the\nentire thing:\n\nWhile this is all good and dandy, itemgetter will raise a KeyError if it can't find the\ncorresponding value against a key in a map or an IndexError if the provided index is\noutside of the range of the sequence. This is how it looks in a dict:\n\nIn the above snippet, itemgetter can't find the key fiz in the dict d and it complains\nwhen we try to fetch the value against it. In a sequence, the error looks like this:\n\nA more tolerant version of 'operator.itemgetter'\n\nI wanted something that works similar to itemgetter but doesn't raise these exceptions\nwhen it can't find the key in a dict or the index of a sequence is out of range. Instead,\nit'd return a default value when these exceptions occur. So, to avoid KeyError in a map,\nit'd use d.get(key, default) instead of d[key] to fetch the value. Similarly, in a\nsequence, it'd first compare the length of the sequence with the index and return a default\nvalue if the index is out of range.\n\nSince operator.itemgetter is a class, we could inherit it and overwrite the __init__\nmethod. However, your type-checker will complain if you do so. That's because, in the stub\nfile, the itemgetter class is decorated with the typing.final decorator and isn't meant\nto be subclassed. So, our only option is to rewrite it. The good news is that this\nimplementation is quite terse just like the original. Here it goes:\n\nThis class behaves almost the same way as the original itemgetter function. The only\ndifference is that you can pass a default value to return instead of raising\nKeyError/IndexError depending on the type of the container. Let's try it out with a dict:\n\nHere, we're trying to access a bunch of keys that don't exist in the dict d and we want to\ndo this without raising any exceptions. You can see that instead of raising an exception,\nsafe_itemgetter returns a tuple containing the value(s) that it can find and the rest of\nthe positions are filled with the default value; in this case, the <NOTHING> sentinel.\nWe can pass any default value there:\n\nThis works similarly when a sequence is passed:\n\nThis returns an empty tuple when the sequence index is out of range. It works with multiple\nindices as well:\n\nFurther reading\n\n- [operator.itemgetter - Python docs]\n\n\n\n\n[operator.itemgetter - Python docs]:\n    https://docs.python.org/3/library/operator.html#operator.itemgetter",
  "title": "Safer 'operator.itemgetter' in Python"
}