{ "cells": [ { "cell_type": "markdown", "metadata": { "nbgrader": { "grade": false, "grade_id": "cell-98b92489332760b6", "locked": true, "schema_version": 3, "solution": false, "task": false } }, "source": [ "# Functions in Python\n", "\n", "```{admonition} Interactive page\n", ":class: warning, dropdown\n", "This is an interactive book page. Press launch button at the top right side.\n", "```" ] }, { "cell_type": "markdown", "metadata": { "nbgrader": { "grade": false, "grade_id": "cell-98b92489332760b6", "locked": true, "schema_version": 3, "solution": false, "task": false } }, "source": [ "## Functions to save typing\n", "\n", "In programming, you often want to **repeat the same sequence of commands** over and over again. \n", "One way to do this is to copy and paste the same piece of code over and over again. This is actually quite easy, but quickly runs into a problem:\n", "* If you want to slightly **change** what the code does, you need to make changes in many places. \n", " * If you change something in one place but forget in another, then your program might give an **error**. \n", " * Or even worse, and even harder to debug, the mistake may give you the **wrong answer** withour giving an error.\n", "\n", "For this reason (among others), programming languages allow programmers to **define functions**. Functions are pieces of code that you can give a name to, and then use time and again, without having to retype or copy-paste the code text. \n", "\n", "As an example, let's say we want to print out the value of variables named `a` and `b` using a long sentence:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "remove-output" ] }, "outputs": [], "source": [ "a = 6\n", "b = 4\n", "print(\"The value of variable a is\", a)\n", "print(\"The value of variable b is\", b)\n", "\n", "a = a/2\n", "b = 3\n", "print(\"The value of variable a is\", a)\n", "print(\"The value of variable b is\", b)\n", "\n", "a = a+1\n", "b = 1.5\n", "print(\"The value of variable a is\", a)\n", "print(\"The value of variable b is\", b)\n", "\n", "a = a-20\n", "b = -1e4\n", "print(\"The value of variable a is\", a)\n", "print(\"The value of variable b is\", b)\n", "\n", "a = a+1j\n", "b = 1\n", "print(\"The value of variable a is\", a)\n", "print(\"The value of variable b is\", b)" ] }, { "cell_type": "markdown", "metadata": { "nbgrader": { "grade": false, "grade_id": "cell-fb10310a1ec1a391", "locked": true, "schema_version": 3, "solution": false, "task": false } }, "source": [ "To save yourself a lot of typing, you can define a simple function to do this work for you. To define a function, you use the following syntax:\n", "\n", "```\n", "def function_name():\n", " ...\n", "```\n", "\n", "Here, you replace the `...` with the code you want the function to execute. The Python code inside the function should be [indented](https://en.wikipedia.org/wiki/Indentation_(typesetting)) (like we've also seen for [conditional statements](../chapter6/conditional-statements.ipynb) and [loops](../chapter6/loops.ipynb)) by starting each line with `Tab` or four spaces (where [consistency](../chapter5/pep-8.ipynb) is really important). Python typically recognizes where indentation should occur and places the cursor for you. Note that Python also tries to detect any inconsistencies in your indentation (e.g., a mixed usage of tabs and spaces). If you make a mistake in such a way that Python cannot understand what you mean, it will give you an `IndentationError`.\n", "\n", "**Indentation in Python is very important**: Python uses indentation to know which code is inside the function and which is not (just like we've seen in flow control). \n", "\n", "Once you have defined your function, you can execute it by using `function_name()`. Let's look at how to use a function as a \"procedure\" to simplify the code above: " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "remove-output" ] }, "outputs": [], "source": [ "def print_status():\n", " print(\"The value of variable a is\", a)\n", " print(\"The value of variable b is\", b)\n", "\n", "a = 6\n", "b = 4\n", "print_status()\n", "\n", "a = a/2\n", "b = 3\n", "print_status()\n", "\n", "a = a+1\n", "b = 1.5\n", "print_status()\n", "\n", "a = a-20\n", "b = -1e4\n", "print_status()\n", "\n", "a = a+1j\n", "b = 1\n", "print_status()" ] }, { "cell_type": "markdown", "metadata": { "nbgrader": { "grade": false, "grade_id": "cell-a12ce8709fcf3b9f", "locked": true, "schema_version": 3, "solution": false, "task": false } }, "source": [ "In this example, it may not be such a big deal, but you can imagine that as the code in your function becomes more and more complicated, it will save you a lot of time. Also, imagine that we wanted to change the wording of the sentence we print: in the case with the function, we would only have to do this once, while in the example without function, we would have to manually change this at five different places. " ] }, { "cell_type": "markdown", "metadata": { "nbgrader": { "grade": false, "grade_id": "cell-cc004d60a3c3b17c", "locked": true, "schema_version": 3, "solution": false, "task": false } }, "source": [ "```{exercise}\n", ":class: dropdown\n", "Write your own function that contains two lines of code. The first line should make a new variable `var2` that converts the given `var` to an integer. The second line of your code should print the value of `var2`. \n", "Using this code, play around with the indentation (add extra tabs and spaces for example) to see how 'critical' Python is with indentation. For example: do three spaces work? Does one space work? What about `Tab` in the first line and three spaces in the second line? \n", "```" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "remove-output" ] }, "outputs": [], "source": [ "var = 3.5\n", "\n", "# Your code below\n" ] }, { "cell_type": "markdown", "metadata": { "nbgrader": { "grade": false, "grade_id": "cell-1be65f1712abb44d", "locked": true, "schema_version": 3, "solution": false, "task": false } }, "source": [ "## Functions with input variables\n", "\n", "In the example above, our function explicitly printed out variables `a` and `b`. But this only works because we know in advance that the person using our function has defined variables `a` and `b`. But what if we wanted to print the value of variable `c`? \n", "\n", "Let's say that we wanted to print out the status of variables whose name we don't know in advance. If we wanted to make a function that could print out a message with the status of value of *any* variable, how could we do this? \n", "To allow functions to be more generic, and therefore more **reusable** in general, Python allows you to **define input variables for your function**. The syntax for this is the following:\n", "\n", "```\n", "def function_name(x):\n", " ...\n", "```\n", "\n", "When you do this, for the code **INSIDE** your function, a variable `x` is defined. `x` will then acquire the value that is given to the function by the user. Let's look at an example:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "remove-output" ] }, "outputs": [], "source": [ "def print_status_2(x):\n", " print(\"The value passed to the function is\", x)\n", "\n", "a = 1.5\n", "print_status_2(a)\n", "\n", "a = 1+1j\n", "print_status_2(a)\n", "\n", "print_status_2(1.5323)" ] }, { "cell_type": "markdown", "metadata": { "nbgrader": { "grade": false, "grade_id": "cell-34acac6dd514c884", "locked": true, "schema_version": 3, "solution": false, "task": false } }, "source": [ "How does this work? \n", "\n", "When the function `print_status(a)` is called, Python **passes (sends) the value of `a` to the function**. Inside the function, Python creates a new (temporary) variable called `x`, that is **defined ONLY while the function code is running**. This temporary variable `x` is then assigned the value that was passed to the function (in this case, the value of `a`), and then the code is executed. When the function is finished, the variable `x` is destroyed. (Try adding the code `print(x)` above outside the function and see what happens!)\n", "\n", "Note that the things you pass to functions don't even need to be variables (see third example above). This is fine because the function only needs the value of the argument that is passed to the function. " ] }, { "cell_type": "markdown", "metadata": { "nbgrader": { "grade": false, "grade_id": "cell-53fd3c06a26175a3", "locked": true, "schema_version": 3, "solution": false, "task": false } }, "source": [ "```{exercise}\n", ":class: dropdown\n", "Copy your code from the previous exercise (with normal indentation) into the cell below and change it such that it uses a function with input parameters to achieve the same task.\n", "```" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Your code here\n" ] }, { "cell_type": "markdown", "metadata": { "nbgrader": { "grade": false, "grade_id": "cell-581873fe20420dc6", "locked": true, "schema_version": 3, "solution": false, "task": false } }, "source": [ "## Functions with multiple inputs \n", "\n", "Functions can also take multiple input variables. To do this, you put them all in between the brackets `()`, separated by commas. For example, with three variables, the syntax is:\n", "\n", "```\n", "def function_name(variable1, variable2, variable3):\n", " ...\n", "```\n", "\n", "After defining it, you would then use this function in the following way:\n", "\n", "```\n", "function_name(argument1, argument2, argument3)\n", "```\n", "\n", "When you do this, inside the function `variable1` will get assigned the value of `argument1`, `variable2` will get assigned the value of `argument2`, and `variable3` will get assigned the value of `argument3`. **This matching of the position in the list is called matching by \"positional order\"**. \n", "\n", "Note that there are several different names used for the \"input variables\" of a function: often, computer scientists use the name \"input arguments\" (or just \"arguments), or \"input parameters\" (or just \"parameters\"). " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "remove-output" ] }, "outputs": [], "source": [ "def print_status3(x, y):\n", " print(\"The value of the first input variable is \", x)\n", " print(\"The value of the second input variable is \", y)\n", "\n", "print_status3(1, 2)\n", "print_status3(2.5, 1.5)\n", "print_status3(a, 2*a)" ] }, { "cell_type": "markdown", "metadata": { "nbgrader": { "grade": false, "grade_id": "cell-78e05b5cbe11902b", "locked": true, "schema_version": 3, "solution": false, "task": false } }, "source": [ "```{exercise}\n", "Make a new function `print_status_4()` that takes three variables as arguments and prints out messages telling the user the values of each of them (as above, but with three input variables). Test it to make sure it works. \n", "```" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Your code here\n" ] }, { "cell_type": "markdown", "metadata": { "nbgrader": { "grade": false, "grade_id": "cell-fbf7e40e800af968", "locked": true, "schema_version": 3, "solution": false, "task": false } }, "source": [ "## Functions that return values\n", "\n", "So far we've mainly been looking at functions that used some input values and then printed something for us. In addition to receiving values as inputs, **functions can also send back values** to the person using the function. In computer programming, this is called the **\"return value\"**. \n", "\n", "When you create a function, you can use the `return` command to specify what value should be sent back to the user. Let's look at an example:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "remove-output" ] }, "outputs": [], "source": [ "def my_formula(x):\n", " y = x**2 + 3\n", " return y" ] }, { "cell_type": "markdown", "metadata": { "nbgrader": { "grade": false, "grade_id": "cell-3da704c82b41c669", "locked": true, "schema_version": 3, "solution": false, "task": false } }, "source": [ "To \"capture\" the value returned by the function, you can assign it to a variable like this:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "remove-output" ] }, "outputs": [], "source": [ "result = my_formula(3.5)\n", "print(result)" ] }, { "cell_type": "markdown", "metadata": { "nbgrader": { "grade": false, "grade_id": "cell-c4eb4137a0421531", "locked": true, "schema_version": 3, "solution": false, "task": false } }, "source": [ "You can also just directly \"use\" the result of the function if you want:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "remove-output" ] }, "outputs": [], "source": [ "print(my_formula(4.6))" ] }, { "cell_type": "markdown", "metadata": { "nbgrader": { "grade": false, "grade_id": "cell-9710d6768449ded8", "locked": true, "schema_version": 3, "solution": false, "task": false } }, "source": [ "Note that as soon as Python sees the `return` command, it stops running the function, so **any code after it will not be executed**:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "remove-output" ] }, "outputs": [], "source": [ "def my_function(x):\n", " print(\"This gets printed.\")\n", " return x**2 + 3\n", " print(\"This does not.\")\n", " \n", "print(my_function(5))" ] }, { "cell_type": "markdown", "metadata": { "nbgrader": { "grade": false, "grade_id": "cell-aef6b10798831fa6", "locked": true, "schema_version": 3, "solution": false, "task": false } }, "source": [ "If you want to send back more than one result to the user of your function, you can separate the results with commas when you use the `return` command. Let's see how to do that:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "remove-output" ] }, "outputs": [], "source": [ "def plus_minus(x, y):\n", " return x+y, x-y\n", "\n", "addition, subtraction = plus_minus(5, 2)\n", "print(\"Sum =\", addition)\n", "print(\"Subtraction =\", subtraction)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice that the order of elements in `return` also matters (\"positional order\")." ] }, { "cell_type": "markdown", "metadata": { "nbgrader": { "grade": false, "grade_id": "cell-c20b50841a685607", "locked": true, "schema_version": 3, "solution": false, "task": false } }, "source": [ "```{exercise}\n", ":class: dropdown\n", "1. Write a function that takes two real numbers as input and returns the sum and product of the two numbers. In your function, send both of the calculated numbers back as a return value. \n", "2. Use your function to calculate the sum and product of `a` and `b`, \"capturing\" the sum and product in variables `s` and `p`.\n", "```" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a = 1.5\n", "b = 2.5\n", "\n", "# Your code here" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "thebe-remove-input-init" ] }, "outputs": [], "source": [ "import micropip\n", "await micropip.install(\"jupyterquiz\")\n", "from jupyterquiz import display_quiz\n", "import json\n", "\n", "with open(\"questions3.json\", \"r\") as file:\n", " questions=json.load(file)\n", " \n", "display_quiz(questions, border_radius=0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Functions that return Booleans\n", "\n", "In the [chapter on Flow control](../chapter6/conditional-statements.ipynb), we mentioned that functions in Python can return Boolean values (`True` or `False`). These return values can be useful for **error handling**, **conditional logic**, and **making decisions** in your code.\n", "\n", "Python functions can perform checks and provide feedback based on the outcome of those checks. For example, a function may check whether a file exists, whether a certain condition is met, or whether an operation was successful. The returned Boolean value can then be used in an `if` statement to control the flow of the program.\n", "\n", "Let's look at a few examples." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Example 1**: Checking conditions\n", "\n", "In the code below, the `is_even` function checks whether a given number is even. It returns `True` for even numbers and `False` for odd numbers. This result is then used in an `if` statement to print whether the number is even or odd." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "remove-output" ] }, "outputs": [], "source": [ "# Define function that checks if a number is even\n", "def is_even(number):\n", " return number % 2 == 0\n", "\n", "# Define a number (you can also try to run code with an odd number)\n", "num = 4\n", "\n", "# Apply function to our number within an if-else statement\n", "if is_even(num):\n", " print(f\"{num} is even.\")\n", "else:\n", " print(f\"{num} is odd.\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Example 2**: Validating user input\n", "\n", "In this example, we created a function to check if the input provided by a user is a valid integer. Note that you may need to run this in VS Code as it contains `input()`.\n", "\n", "In this code, the `is_valid_integer` function attempts to convert the input value to an integer using `try`. If successful, it returns `True`; if it raises a `ValueError`, it returns `False`. This Boolean return value is then used to determine if the user input is valid.\n", "\n", "Note: as this code uses `input()`, try it out in VS Code." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "remove-output" ] }, "outputs": [], "source": [ "# Define function that checked if a vlue is integer\n", "def is_valid_integer(value):\n", " try:\n", " int(value)\n", " return True\n", " except ValueError:\n", " return False\n", "\n", "# Ask user for input value\n", "user_input = input(\"Enter an integer: \")\n", "\n", "# Apply function to user_input within an if-else statement\n", "if is_valid_integer(user_input):\n", " print(f\"You entered a valid integer: {user_input}\")\n", "else:\n", " print(\"That's not a valid integer!\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Example 3** *(Optional)*: Checking if a file exists\n", "\n", "A common usage scenario for functions that return Booleans is checking whether a file exists.\n", "\n", "The `file_exists` function below returns `True` if the specified file exists and `False` otherwise. \n", "\n", "Note that this code also uses a [Python package called `os`](https://docs.python.org/3/library/os.html), which is useful for interacting with the operating system. It contains functions that perform many OS-related tasks, such as file, directory, and path manipulation. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "remove-output" ] }, "outputs": [], "source": [ "import os\n", "\n", "# Define function that returns Booleand checking whether a file exists\n", "def file_exists(filepath):\n", " return os.path.isfile(filepath)\n", "\n", "# File whose existence we want to check\n", "file_path = \"example.txt\"\n", "\n", "# Applying function within an if-else statement\n", "if file_exists(file_path):\n", " print(f\"{file_path} exists.\")\n", "else:\n", " print(f\"{file_path} does not exist.\")\n" ] } ], "metadata": { "jupytext": { "formats": "ipynb,md" }, "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.11.4" } }, "nbformat": 4, "nbformat_minor": 4 }