๐Ÿ“ Python

Function Composition

P
Author
PyLand Team
๐Ÿ“…
Published
03.04.2026
โฑ๏ธ
Reading time
3 min
๐Ÿ‘๏ธ
Views
386
๐ŸŒณ
Level
Advanced

Function composition connects small functions so that one function’s result becomes the next function’s argument.

What Is Function Composition?

Function composition is the practice of combining simple functions into more complex ones.

In math: (f โˆ˜ g)(x) = f(g(x)) โ€” first g, then f.

def add_10(x):
    return x + 10

def multiply_2(x):
    return x * 2

# Manual composition: first add_10, then multiply_2
result = multiply_2(add_10(5))
print(result)  # 30
# 5 โ†’ add_10 โ†’ 15 โ†’ multiply_2 โ†’ 30

Why Use Composition?

Without composition the same pipeline is written out by hand every time:

data1 = step1(raw_data)
data2 = step2(data1)
result = step3(data2)

# For another dataset โ€” again:
data1 = step1(other_data)
data2 = step2(data1)
result = step3(data2)

With composition โ€” define the pipeline once, reuse it everywhere:

process = pipe(step1, step2, step3)

result1 = process(raw_data)
result2 = process(other_data)
result3 = process(more_data)

compose() and pipe()

compose() โ€” right to left

def compose(*functions):
    """Apply functions right to left: f(g(h(x)))."""
    def composed(x):
        result = x
        for func in reversed(functions):
            result = func(result)
        return result
    return composed

def add_10(x):
    return x + 10

def multiply_2(x):
    return x * 2

def square(x):
    return x ** 2

# compose: the last argument runs first
pipeline = compose(square, multiply_2, add_10)
print(pipeline(5))  # 900
# 5 โ†’ add_10 โ†’ 15 โ†’ multiply_2 โ†’ 30 โ†’ square โ†’ 900

Reads right to left (mathematics convention).

pipe() โ€” left to right

def pipe(*functions):
    """Apply functions left to right."""
    def piped(x):
        result = x
        for func in functions:
            result = func(result)
        return result
    return piped

# pipe: the first argument runs first
pipeline = pipe(add_10, multiply_2, square)
print(pipeline(5))  # 900
# 5 โ†’ add_10 โ†’ 15 โ†’ multiply_2 โ†’ 30 โ†’ square โ†’ 900

Reads left to right โ€” more natural for code.


Practical Examples

Example 1: String cleaning

import string

def trim(text):
    return text.strip()

def lowercase(text):
    return text.lower()

def remove_punctuation(text):
    return text.translate(str.maketrans("", "", string.punctuation))

clean_text = pipe(trim, lowercase, remove_punctuation)

dirty = "  Hello, World!  "
print(clean_text(dirty))  # "hello world"

Example 2: Price calculation

def validate_positive(x):
    if x <= 0:
        raise ValueError("Must be positive")
    return x

def apply_discount(percent):
    def discount(price):
        return price * (1 - percent / 100)
    return discount

def add_tax(percent):
    def tax(price):
        return price * (1 + percent / 100)
    return tax

def round_price(price):
    return round(price, 2)

# Pipeline: validate โ†’ 20% discount โ†’ 10% tax โ†’ round
calculate_price = pipe(
    validate_positive,
    apply_discount(20),
    add_tax(10),
    round_price
)

print(calculate_price(100))  # 88.0
# 100 โ†’ validate โ†’ 80 โ†’ 88 โ†’ 88.0

Decorators Are Composition Too

def uppercase_decorator(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs).upper()
    return wrapper

def exclaim_decorator(func):
    def wrapper(*args, **kwargs):
        return f"{func(*args, **kwargs)}!!!"
    return wrapper

@exclaim_decorator
@uppercase_decorator
def greet(name):
    return f"hello, {name}"

print(greet("Alice"))  # "HELLO, ALICE!!!"
# greet โ†’ uppercase โ†’ exclaim

The decorator stack is applied bottom to top: uppercase_decorator first, then exclaim_decorator.


Common Mistakes

Mistake 1: Wrong argument order

def add_10(x):
    return x + 10

def multiply_2(x):
    return x * 2

# compose reads RIGHT TO LEFT โ€” multiply_2 runs first!
wrong = compose(add_10, multiply_2)
print(wrong(5))  # 20  (5*2=10, 10+10=20)

# pipe reads LEFT TO RIGHT โ€” add_10 runs first
right = pipe(add_10, multiply_2)
print(right(5))  # 30  ((5+10)*2=30)

Mistake 2: Incompatible function signatures

def add(a, b):    # takes 2 arguments
    return a + b

def square(x):    # takes 1 argument
    return x ** 2

# โŒ ERROR: square would receive a tuple instead of a number
# pipeline = pipe(add, square)

# โœ… CORRECT: fix one argument with partial
from functools import partial

add_10 = partial(add, 10)
pipeline = pipe(add_10, square)
print(pipeline(5))  # 225  ((5+10)^2)

Function composition is the foundation of functional style in Python. Use pipe() for readable transformation chains and compose() when mathematical ordering matters.

Your reaction to the article

๐Ÿ’ฌ Comments (0)

๐Ÿ” Sign in to leave a comment
๐Ÿšช Login
๐Ÿ’ญ

No comments yet

Be the first to share your opinion about this article!

๐Ÿ”— Similar

Similar articles

Continue learning with these materials

๐Ÿ“

How Functions Work Together in Python ๐Ÿงฉ

A small function normally handles one clear task. A real program, however, contains several tasks:...

๐Ÿ“… 09.08.2026 ๐Ÿ‘๏ธ 78
๐Ÿ“

map() โ€” Transform Every Element!

The map() function lazily applies a transformation to every item in an iterable.

๐Ÿ“… 03.04.2026 ๐Ÿ‘๏ธ 429
๐Ÿ“

filter() โ€” Pick What You Need!

The filter() function keeps the elements for which a predicate returns a truthy value.

๐Ÿ“… 03.04.2026 ๐Ÿ‘๏ธ 429