Building Function Pipelines

Introduction: From One Big Function to a Chain of Small Ones

Welcome back to Writing Complex Python Functions! We have reached the sixth and final unit of the course. In the previous unit, we packed validation, filtering, and several accumulators into one focused function and argued that length is not a defect as long as the function has a single job. This unit makes the opposite move: we keep several small functions, each with one job, and wire them together so that one function's output becomes the next one's input. That chain is called a pipeline.

Our program for this unit ships with four such stages and one line that connects them. Pressing Run prints:

--- Results ---
1. 95
2. 72
3. 64

Two questions guide everything that follows: what makes a set of functions composable, and what determines the order in which they must be called? Since this is the closing unit, the answers pull together conditionals, loops, list parameters, new-list returns, and documented contracts all at once.

The Four Stages and Their Contracts

The trick to reading a pipeline is to stop reading bodies and start reading contracts: what goes in and what comes out. Here are the first two stages, both in the exact loop-and-accumulator shape we built in earlier units.

def to_numbers(lines):
    """Turn a list of number strings into a new list of integers."""
    numbers = []
    for line in lines:
        numbers.append(int(line.strip()))
    return numbers


def keep_passing(scores, threshold=60):
    """Return a new list holding only the scores that meet the threshold."""
    result = []
    for score in scores:
        if score >= threshold:
            result.append(score)
    return result

Neither function is new territory: an accumulator before the loop, work inside, and one dedented return after it. What is new is how we will use them. Notice that to_numbers hands back integers, while keep_passing expects integers; that match is the seam where the two stages can be joined.

Two More Stages: Ranking and Formatting

The remaining two stages complete the chain. The first reorders the numbers, and the second turns them into something a person can read.

def rank(scores):
    """Return the scores sorted from highest to lowest (a new list)."""
    return sorted(scores, reverse=True)


def format_report(scores):
    """Build one printable multi-line report string from ranked scores."""
    lines = ["--- Results ---"]
    position = 1
    for score in scores:
        lines.append(str(position) + ". " + str(score))
        position += 1
    return "\n".join(lines)

rank is a one-liner: numbers in, a new descending list out. format_report starts its own lines accumulator with the header, appends one "1. 95"-style entry per score using a position counter, and joins everything with "\n" into a single string. It is the end of the line: it takes numbers but hands back text, so nothing numeric can follow it.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal