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.
๐ฌ Comments (0)
No comments yet
Be the first to share your opinion about this article!