Python notes

the_power_of_reduce

The power of reduce

  • reduce was a built-in function in Python 2; moved to functools in Python 3.

  • reduce applies a binary function to items of an iterable to reduce them to a single value.

  • Simplified implementation:

    def reduce(function, iterable, initial_value):
        result = initial_value
        for value in iterable:
            result = function(result, value)
        return result
    >>> reduce(operator.add, range(10))  # 0+1+2+...+9
    45
    # basically
    def sum(iterable, start=0):
        acc = start
        for elem in iterable:
            acc = acc + elem
        return acc

  • Lambda equivalents using reduce:

    all = lambda iterable: reduce(operator.and_, iterable)
    any = lambda iterable: reduce(operator.or_, iterable)
    max_ = lambda a, b: a if a >= b else b
    max = lambda iterable: reduce(max_, iterable)
    min_ = lambda a, b: a if a <= b else b
    min = lambda iterable: reduce(min_, iterable)
    sum = lambda iterable: reduce(operator.add, iterable)

“Fun” or Far-Fetched Reductions

  • First/Last element:

    reduce(lambda l, r: l, iterable)  # first element
    reduce(lambda l, r: r, iterable)  # last element
  • Building tuples using reduce:

    reduce(lambda t, v: t + (v,), range(10), ())

The Identity Element and reduce's Third Argument

  • Third argument = initializer; used when the iterable might be empty.

  • If omitted and iterable is empty → TypeError.

  • Identity element: a value that doesn’t affect the result of the reduction.

    Examples:

    • Addition → 0
    • Multiplication → 1
    • all() → True
    • any() → False
  • For max/min:

    • Identity: -inf for max, inf for min
    • These are not used automatically because of type compatibility issues (e.g., strings)

Reductions Without Identity Elements

  • Some reductions (like “last element”) lack a neutral identity.
  • Must handle empty iterables via conditionals or try/except.

Example: Accessing Nested Dictionaries

d = {"one": {2: {"c": {4: 42}}}}
keys = ["one", 2, "c", 4]
reduce(dict.get, keys, d)  # → 42

Example: Accessing Nested Class Attributes

class C: pass

c = C()
c.one = C()
c.one._2 = C()
c.one._2.c = C()
c.one._2.c._4 = 42

attrs = ["one", "_2", "c", "_4"]
reduce(getattr, attrs, c)  # → 42