Skip to content
Lesson 9 of 12

Step 1 of 7 · Reading · ~4 min

Learn

Functions

Higher-Order Functions

You will be able to treat functions as data: pick one from a table, hand one to sorted, build a new one on demand. You have already passed functions around with Counter(words) and sorted(x, reverse=True) sitting beside them. A function that takes or returns a function is called higher-order. A nested function reading its enclosing variable was the E in LEGB last lesson; here it is the point.

Functions are values

A function name is a variable like any other, so a function can sit in a dict. That gives you a dispatch table, a lookup that replaces an if/elif chain:

python
upper DATA LAB
size 8

ops[name] is the function itself; the parentheses call it.

key=: sort by a computed value

sorted, min and max call your key function once per item and compare the results. A tuple key compares field by field, so the first field wins and later ones break ties:

python
[('Dune', 1965, 4.5), ('Beloved', 1987, 4.5), ('Emma', 1815, 4.0), ('Ulysses', 1922, 4.0)]

A lambda is a function with no name and exactly one expression. If it needs a second line, or you use it twice, write a def with a name. For plain "item number i" keys, operator.itemgetter(2, 1) is the ready-made version and returns the tuple (rating, year).

Negating only works on numbers. key=lambda s: -s on strings fails with TypeError: bad operand type for unary -: 'str', so a tuple key cannot sort text in descending order.

Ties, reverse=True and several passes

Sorting is stable: items with equal keys keep their input order. reverse=True keeps that promise too. It does not flip the ties:

python

Reversing the finished list turns pear, kiwi, plum into plum, kiwi, pear. That is a different answer.

Stability gives you the way to mix directions: sort several times, least important key first, so the last pass is the most important and its ties inherit the earlier order. Shelf ascending, title descending:

python

Run the passes the other way round and the title pass wins outright, scattering the shelves: [('art', 'Moore'), ('sci', 'Emma'), ('sci', 'Dune'), ('sci', 'Cosmos'), ('art', 'Arp')].

map, filter, reduce, partial

map(f, items) and filter(f, items) return one-shot iterators, not lists. Use them when f already exists (list(map(int, parts))); otherwise a comprehension reads better.

python

functools.reduce(f, items, start) folds a sequence into one value by calling f(result_so_far, next_item). reduce(math.gcd, [12, 18, 30]) is 6. Without start, an empty list raises TypeError: reduce() of empty iterable with no initial value; reduce(math.gcd, [], 0) gives 0.

functools.partial(f, ...) fixes some arguments in advance: to_bin = partial(int, base=2) makes to_bin("1011") return 11.

Closures: functions that build functions

python

wrap keeps tag alive after make_tagger has returned. That pairing is a closure. It captures the variable, not its value at that moment, which is the trap:

python

Common mistakes

  • Reversing a sorted list to get descending order. Ties come out backwards (plum, kiwi, pear); use reverse=True on the pass instead.
  • Running multi-pass sorts most important key first. The last pass dominates, so the output groups by the wrong field.
  • Negating a text key. -name raises TypeError; descending text needs reverse=True in a pass of its own.
  • Reading a map object twice, or building closures in a loop without freezing the loop variable.

Your exercise

Sort employee rows by a spec such as dept,-salary,name, where any field can be descending. The starter parses the rows and the spec and prints the rows unsorted; you decide how each field name becomes a key function and in what order the passes run.

Up nextTry and ExceptError Handling

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…