{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Combining functions\n",
"A quite common pattern in Python is to directly use the result of one function as a parameter for another function. This\n",
"unit focuses on this pattern. Furthermore, this unit also shows when not to combine functions.\n",
"\n",
"\n",
"## Introduction\n",
"Using the result of one function as a parameter for another function is something that was used already in previous\n",
"units. For example, to get a input from the user and convert it to an integer the following Python snippet can be used."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"number = int(input(\"Please enter a number: \"))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Although this snippet should look familiar by now, there are a few things to note. First, two functions are used in the snippet:\n",
"\n",
"- `input()` - to output a message and read input from the user\n",
"- `int()` - to convert the user input to an integer.\n",
"\n",
"Second, the return value of the `input()` function is passed as a parameter to the `int()` function. This becomes obvious if an \n",
"auxiliary variable is used."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"user_input = input(\"Please enter a number: \")\n",
"number = int(user_input)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Third, the evaluation of the function is performed from the inside out. I.e. that the innermost function, in our example\n",
"the `input()` function is evaluated first. The outer function (`int()` in the example) is evaluated as soon as a result\n",
"is returned from the `input()` function."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Nesting multiple functions\n",
"\n",
"Of cause, it is not only possible to combine two functions. This is shown in the example below."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def multiply(a, b):\n",
" return a * b\n",
"\n",
"\n",
"print(\n",
" \"The product of the two numbers is\",\n",
" multiply(\n",
" int(input(\"Please enter a number: \")),\n",
" int(input(\"Please enter another number:\")),\n",
" ),\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In the beginning of this example,the function `multiply()` is defined. The function expects two parameters, the factors\n",
"`a` and `b`. Next, the function is used to implement the following functionally:\n",
"1. Ask the user to input a number\n",
"1. Ask the user to input another number\n",
"1. Print the product of the two numbers\n",
"\n",
"To implement this functionality, the following three functions are used in succession:\n",
"- `input()`\n",
"- `int()`\n",
"- `print()`\n",
"\n",
"Again, the evaluation of the combined function call is performed starting with the innermost function. In the example\n",
"the functions are evaluated in the following order:\n",
"\n",
"1. The first `input()` function is evaluated (i.e. `input(\"Please enter a number: \")`)\n",
"1. The result of the `input()` function is passed to the `int()` function\n",
"1. The second `input()` function is evaluated (i.e. `input(\"Please enter another number: \")`)\n",
"1. The result of the `input()` function is passed to the `int()` function\n",
"1. The `multiply()` function is evaluated\n",
"1. The `print()` function is evaluated"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Caveats\n",
"The previous examples show one of the important caveats when combining functions. Combining of functions can be used to\n",
"make the Python code very concise. In case of combing the `int()` and the `input()` function, this helps the reader to\n",
"quickly understand the purpose of the Python code. In case of the previous example the combination of multiple functions\n",
"actually hinder understanding of the code. In this example it is not immediately clear to the reader what exactly\n",
"happens.\n",
"\n",
"In summary, combining functions is a nice possibility to write very concise code. However, misusing the combination of\n",
"functions quickly leads to unreadable Python code. When to combine functions and when not to combine functions is\n",
"something that requires experience. In general it is better to split the Python code in to multiple lines by using\n",
"auxiliary variables then trying to write overly concise programs.\n",
"\n",
"Always remember: [With great power comes great responsibility](https://en.wikipedia.org/wiki/With_great_power_comes_great_responsibility)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Combining functions\n",
"\n",
"Of course, it is also possible to combine functions without nesting. Inside the body of a function it is possible to invoke\n",
"other functions. This is shown in the following cell."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def is_divisible(a, b):\n",
" \"\"\"\n",
" Checks if a is divisible by b without a remainder\n",
" \"\"\"\n",
" return a % b == 0\n",
"\n",
"\n",
"def is_prime(n):\n",
" \"\"\"\n",
" Checks if the number n is a prime number\n",
" \"\"\"\n",
" for i in range(2, n):\n",
" if is_divisible(n, i):\n",
" return False\n",
" return True\n",
"\n",
"\n",
"print(f\"10 is a prime: {is_prime(10)}\")\n",
"print(f\"17 is a prime: {is_prime(17)}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Exercise: Calculating the binomial coefficient\n",
"\n",
"The [binomial coefficient](https://en.wikipedia.org/wiki/Binomial_coefficient) can be used to calculate how many number of ways $k$ objects can be chosen from \n",
"a set of $n$ objects. This is for example the case when playing the lottery. Using the binomial coefficient it is \n",
"possible to calculate the possible combination for the German lottery. Here 6 numbers are drawn from the numbers \n",
"1 to 49. \n",
"\n",
"The number of possible combinations is given by the formula: $\\frac{49!}{6!(49 - 6)!}$. The general formula for calculating\n",
"the binomial coefficient is: $\\frac{n!}{k!(n - k)!}$ \n",
"\n",
"Write a function `binomial`, that takes the numbers 'n' and 'k' as a parameter and calculates the binomial coefficient \n",
"as a result. Reuse your factorial function from the unit 3 of the current week. Alternatively use the example \n",
"given below as a starting point. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def factorial(n):\n",
" result = 1\n",
"\n",
" if n > 1:\n",
" for i in range(1, n + 1):\n",
" result *= i\n",
"\n",
" return result\n",
"\n",
"\n",
"def binomial(k, n):\n",
" n_fac = factorial(n)\n",
" k_fac = factorial(k)\n",
" n_minus_k_fac = factorial(n - k)\n",
"\n",
" binomial_cof = n_fac / (k_fac * n_minus_k_fac)\n",
"\n",
" return binomial_cof\n",
"\n",
"\n",
"print(\n",
" f\"The possible number of combinations to draw 6 numbers from 49 numbers is {int(binomial(6, 49))}.\"\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Common patterns\n",
"With the information above it is now possible to analyse a quite common pattern used in many Python 🐍 programs. As shown in\n",
"the following example variables and parameters often use the same name. In the example, a global variable `song` is\n",
"defined in the first line. Furthermore, a parameter `song` is defined in the function `play_music` and a local variable\n",
"`song` inside the function `play_ramones()`.\n",
"\n",
"When the function `play_ramones()` *at the end of the cell* is invoked the following happens:\n",
"1. The local variable `song` is initialized with the value \"Blitzkrieg Bop\" *within the function* `play_ramones()`\n",
"1. The function `play_music()` is invoked and the local variable `song` is passed as a parameter *last statement in the function* `play_ramones()`. Through\n",
" this invocation the value of the parameter `song` is set to the value of the local variable `song`\n",
"1. The `print()` function is invoked and the parameter `song` is passed as a parameter *first line of function* `play_music()`.\n",
"\n",
"The global variable `song` is never read or changed. The local variable `song` in the function `play_ramones()` and the\n",
"parameter `song` of the function `play_music` hide the global variable.\n",
"\n",
"It is important to note that, although the name *`song`* of the global variable, the local variable and the parameter is\n",
"the same, they are very different from the point of view of the Python interpreter. Using the same name is just a hint\n",
"for humans reading the program to understand how values are passed along."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"song = \"\"\n",
"\n",
"\n",
"def play_music(song):\n",
" print(\"Listening to\", song)\n",
"\n",
"\n",
"def play_ramones():\n",
" song = \"Blitzkrieg Bop\"\n",
" play_music(song)\n",
"\n",
"\n",
"play_ramones()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"interpreter": {
"hash": "9fd0e282e2343d8b38b390b803aabc7fcea80a18eee8e5bd23ce64f6435b30a1"
},
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.10"
}
},
"nbformat": 4,
"nbformat_minor": 2
}