I was reading about augmented assignment statements like x += 1 and how they execute the in-place versions of operations (e.g. __iadd__ instead of __add__ for addition) when I came across the operator standard library.

At first, I couldn't understand why anyone would use this library. The library's functions all implement common operator symbols but take up more space than the original symbols. Why would anyone choose to write add(x, y) instead of x + y or eq(x, y) instead of x == y? Ironically, that's exactly when I ran across examples in my own code where I should have used the operator library.

Replacing lambdas

During Advent of Code one year, we had to run a set of assembly-like instructions composed of opcodes and operands. An instruction might look like "123" and be broken up into the following parts:

  • 1: opcode that denotes the addition operation
  • 2: first operand
  • 3: second operand

So the input instruction of "123" would translate to the output "5" (2 + 3 = 5) and then that output would be stored somewhere so that it could be used later in the program.

For these challenges, I ended up with a function that looked like this:

OPCODE_TO_FUNC = {
    1: lambda x, y: x + y,
    2: lambda x, y: x * y,
    5: lambda x: x != 0,
    6: lambda x: x == 0,
}

def perform(opcode, *operands):
    return OPCODE_TO_FUNC[opcode](*operands)

See those lambdas? They could have just as easily been performed using functions from the operator library:

import operator
from functools import partial


OPCODE_TO_FUNC = {
    1: operator.add,
    2: operator.mul,
    5: partial(operator.ne, 0),
    6: partial(operator.eq, 0),
}

def perform(opcode, *operands):
    return OPCODE_TO_FUNC[opcode](*operands)

Tada! It comes with some perks too: 1) the operator version executes faster than the lambda version, and 2) the operator functions are pickle-able, while lambdas are definitely not.

Sorting

Another place where the operator library comes in handy is with sorting tuples. Here's an example using itemgetter:

import operator

populations = [
    ("Seattle", 724_000),
    ("Boston",  684_000),
    ("Chicago", 2_710_000),
]

# Sorts alphabetically (using first item of each tuple)
populations.sort()
# [('Boston', 684000), ('Chicago', 2710000), ('Seattle', 724000)]

# Sorts by population (using second item of each tuple)
populations.sort(key=operator.itemgetter(1))
# [('Boston', 684000), ('Seattle', 724000), ('Chicago', 2710000)]

I know that a dictionary would make a better data structure for storing populations like this, but I'm using this example to illustrate the concept. No need to write a lambda for the sort key when itemgetter already exists!