# 9. Function Composition In Python

By **Bernd Klein**. Last modified: 14 Nov 2023.

## In Mathematics

In this section of our Python tutorial we cover the definition of functions. This is also known in mathematics and essentially the same thing as what we will see in Python.

In Mathematics, **function composition** is an operation that combines two or more functions to create a new function. The composition of functions is denoted by a small circle (∘) or by simply placing one function inside another. Given two functions, f and g, the composition of these functions, denoted as (f ∘ g)(x), is defined as follows:

For two functions:

$f: A \to B$

$g: B \to C$

The composition of these functions, $(f ∘ g)$, is a new function that maps elements from set $A$ to set $C$. This composition is defined for an element $x$ in set $A$ as follows:

$(f ∘ g)(x) = f(g(x))$

In other words, to find the value of the composite function $(f ∘ g)$ at a particular input $x$, you first apply the function $g$ to $x$, and then apply the function $f$ to the result of $g(x)$.

Here's an example to illustrate function composition:

Let:

$f(x) = 2x + 3$

$g(x) = x^2$

To find $(f ∘ g)(x)$, you first apply $g$ to $x$:

$g(x) = x^2$

Then, apply $f$ to the result of $g(x)$:

$f(g(x)) = f(x^2) = 2(x^2) + 3$

So, $(f ∘ g)(x) = 2x^2 + 3$, which is the composite function of $f$ and $g$.

Live Python training

Enjoying this page? We offer **live Python training courses** covering the content of this site.

## Function Composition in Python

Function composition in Python is quite similiar to the mathematical concept.

Composition is the combination of two or more functions into one function, whereas currying is the process of turning a function that takes multiple arguments into a sequence of functions, each accepting one argument.

We define the composition h of two functions f and g

$h(x) = g(f(x))$

in the following Python example.

The composition of two functions is a chaining process in which the output of the inner function becomes the input of the outer function.

In Python, you can perform function composition by defining a new function that combines two or more functions to create a composite function. Here's a simple example of how to perform function composition in Python:

```
def f(x):
return 2 * x
def g(x):
return x + 3
# Define a composite function (f ∘ g)
def composite_function(x):
return f(g(x))
# Test the composite function
result = composite_function(5)
print(result)
```

### OUTPUT:

16

This is a simple illustration of function composition in Python. You can create more complex compositions by combining functions in a similar manner, and this technique is especially useful for building more complex operations from simpler building blocks.

Yet, there is a better way to implement the composition of to functions in a more generic way. We define a function `compose`

which takes two functions as parameter and returns a reference to the composition of the two functions:

```
def compose(g, f):
def h(x):
return g(f(x))
return h
```

We will use our compose function in the next example. Let's assume, we have a thermometer measuring degrees Celsius, which is not working accurately. The correct temperature can be calculated by applying the function *readjust* to the temperature values. Let us further assume that we have to convert our temperature values from Celsius to Fahrenheit. We can do this by applying *compose* to both functions:

```
def celsius2fahrenheit(t):
return 1.8 * t + 32
def readjust(t):
return 0.9 * t - 0.5
convert = compose(celsius2fahrenheit, readjust)
measurement_of_thermometer = 10
print(convert(measurement_of_thermometer))
```

### OUTPUT:

47.3

Without using `convert`

we would have to do the following:

```
celsius2fahrenheit(readjust(measurement_of_thermometer))
```

### OUTPUT:

47.3

The composition of two functions is generally not commutative, i.e. compose(celsius2fahrenheit, readjust) is different from compose(readjust, celsius2fahrenheit)

```
convert2 = compose(readjust, celsius2fahrenheit)
print(convert2(measurement_of_thermometer))
```

### OUTPUT:

44.5

`convert2`

is not a solution to our problem, because it is not readjusting the original temperatures of our thermometer but the transformed Fahrenheit values.

## "compose" with Arbitrary Arguments

The function `compose`

which we have just defined can only cope with single-argument functions. We can generalize our function *compose* so that it can cope with all possible functions, along with an example using a function with two parameters.

```
def compose(g, f):
def h(*args, **kwargs):
return g(f(*args, **kwargs))
return h
```

```
def BMI(weight, height):
return weight / height**2
def evaluate_BMI(bmi):
if bmi < 15:
return "Very severely underweight"
elif bmi < 16:
return "Severely underweight"
elif bmi < 18.5:
return "Underweight"
elif bmi < 25:
return "Normal (healthy weight)"
elif bmi < 30:
return "Overweight"
elif bmi < 35:
return "Obese Class I (Moderately obese)"
elif bmi < 40:
return "Obese Class II (Severely obese)"
else:
return "Obese Class III (Very severely obese)"
f = compose(evaluate_BMI, BMI)
again = "y"
while again == "y":
weight = float(input("weight (kg) "))
height = float(input("height (m) "))
print(f(weight, height))
again = input("Another run? (y/n)")
```

### OUTPUT:

Normal (healthy weight)

Live Python training

Enjoying this page? We offer **live Python training courses** covering the content of this site.

Upcoming online Courses

## Composing an arbitrary Number of Functions

```
def compose(*callables):
def composition(x):
for callable in callables:
x = callable(x)
return x
return composition
```

```
def meter2centimeter(dist):
""" Converting m to cm """
return dist * 100
def centimeter2feet(dist):
""" Converting cm to ft """
return dist / 30.48
def feet2inches(dist):
""" Converting ft to in """
return dist * 12
compose(feet2inches, centimeter2feet, meter2centimeter)(1.4)
```

### OUTPUT:

55.11811023622046

We can do the same and more elegantly with the function `reduce`

from the module `functools`

:

```
from functools import reduce
def apply(x, f):
return f(x)
def compose(*callables):
return lambda x: reduce(apply, callables, x)
print(compose(feet2inches, centimeter2feet, meter2centimeter)(1.4)) # => 6
```

### OUTPUT:

55.11811023622046

## Exercises

### Exercise 1

Write three functions:

- A function converting days to hours
- A function converting hours to minutes
- A function converting minutes to seconds

Use compose to write a function to convert days to seconds.

### Exercise 2

Write analogously three functions to be able to convert seconds into days by an appropriate composition function.

Live Python training

Enjoying this page? We offer **live Python training courses** covering the content of this site.

## Solutions

### Exercise 1

We use the compose function, which we have defined in this chapter.

```
def compose(*callables):
def composition(x):
for callable in callables:
x = callable(x)
return x
return composition
def days2hours(days):
""" A function converting days to hours. """
return days * 24
def hours2minutes(hours):
""" A function converting hours to minutes """
return hours * 60
def minutes2seconds(minutes):
""" A function converting minutes to seconds """
return minutes * 60
compose(minutes2seconds, hours2minutes, days2hours)(5)
```

### OUTPUT:

432000

### Exercise 2

```
def hours2days(hours):
""" A function converting hours to days. """
return hours / 24
def minutes2hours(minutes):
""" A function converting minutes to hours """
return minutes / 60
def seconds2minutes(seconds):
""" A function converting seconds to minutes"""
return seconds / 60
compose(hours2days, minutes2hours, seconds2minutes)(432000)
```

### OUTPUT:

5.0

The next solution uses `lambda`

to accomplish the same effect:

```
compose(lambda x: x/60, lambda x: x/60, lambda x: x/24)(432000)
```

### OUTPUT:

5.0

Directly without compose and using `lambda`

is also possible of course, but be warned: Most people would consider this as bad design:

```
compose(lambda x: x/60, lambda x: x/60, lambda x: x/24)(432000)
```

### OUTPUT:

5.0

Live Python training

Enjoying this page? We offer **live Python training courses** covering the content of this site.

Upcoming online Courses