{ "metadata": { }, "nbformat": 4, "nbformat_minor": 5, "cells": [ { "id": "metadata", "cell_type": "markdown", "source": "
Functions are the basic unit of all work in Python! Absolutely everything you do uses functions. Conceptually, functions are super simple. Just like in maths, they take an input, do some transformation, and return an output. As an example, f(x) = x + 2
is a function that calculates whatever value you request, plus two. But functions are foundational, so, you should understand them well before moving on.
\n\nAgenda\nIn this tutorial, we will cover:
\n\n
Functions are a way to re-use some computation you want to do, multiple times. If you don’t have a function, you need to re-write the calculation every time, so we use functions to collect those statements and make them easy to re-run. Additionally they let us “parameterise” some computation. Instead of computing the same value every time, we can template it out, we can re-run the computation with new inputs, and see new results.
\n\n\n\n\nInput: Math\n\n# Define our function\nf(x) = 3 * x\n# Compute some value\nf(3) # is 9\n
\n\nOutput: Python\n\n# Define our function\ndef f(x):\n return 3 * x\n# Compute some value\nf(3)\n
We’ve talked about mathematical functions before, but now we’ll talk about more programing-related functions
\nHuman beings can only keep a few items in working memory at a time. Breaking down larger/more complicated pieces of code in functions helps in understanding and using it. A function can be re-used. Write one time, use many times. (Known as staying Don’t Repeat Yourself (DRY) in the industry.)
\nKnowing what Americans mean when they talk about temperatures and weather can be difficult, but we can wrap the temperature conversion calculation (\\(^{\\circ}\\text{C} = (^{\\circ}\\text{F} - 32) * \\dfrac{5}{9}\\)) up as a function that we can easily re-use.
\n", "cell_type": "markdown", "metadata": { "editable": false, "collapsed": false } }, { "id": "cell-1", "source": [ "def fahr_to_celsius(temp):\n", " return ((temp - 32) * (5/9))" ], "cell_type": "code", "execution_count": null, "outputs": [ ], "metadata": { "attributes": { "classes": [ "" ], "id": "" } } }, { "id": "cell-2", "source": "\nThe function definition opens with the keyword def
followed by the name of the function fahr_to_celsius
and a parenthesized list of parameter names temp
. The body of the function — the statements that are executed when it runs — is indented below the definition line. The body concludes with a return
keyword followed by the return value.
When we call the function, the values we pass to it are assigned to those variables so that we can use them inside the function. Inside the function, we use a return statement to send a result back to whoever asked for it.
\nLet’s try running our function.
\n", "cell_type": "markdown", "metadata": { "editable": false, "collapsed": false } }, { "id": "cell-3", "source": [ "fahr_to_celsius(32)\n", "print(f\"freezing point of water: {fahr_to_celsius(32)}C\")\n", "print(f\"boiling point of water: {fahr_to_celsius(212)}C\")" ], "cell_type": "code", "execution_count": null, "outputs": [ ], "metadata": { "attributes": { "classes": [ "" ], "id": "" } } }, { "id": "cell-4", "source": "\n\n\nThere are several ways to print out a few values in python. We’d recommend you to use
\nf-strings
as it’s the cleanest and most modern way to do it.\n
f
-strings start with anf
(very descriptive name eh?). Within the text between the single or double quotes ('
/\"
) you can use curly braces to refer to variables or python code which will be placed there in the string.\na = 10\nb = f\"Here is the value of a: {a}\"\nprint(b)\nprint(f\"Here is the value of a: {5 + 5}\")\nprint(f\"Here is the value of a: {function_that_returns_10()}\")\n
All of those would print out
\nHere is the value of a: 10
.f-strings can be a lot fancier for formatting decimal places, but we don’t need that for now. Just know:
\n\n
\n- Start with an
\nf
- Use braces to use the value of a variable, a function, or some python expression.
\n
We’ve successfully called the function that we defined, and we have access to the value that we returned.
\nNow that we’ve seen how to turn Fahrenheit into Celsius, we can also write the function to turn Celsius into Kelvin:
\n", "cell_type": "markdown", "metadata": { "editable": false, "collapsed": false } }, { "id": "cell-5", "source": [ "def celsius_to_kelvin(temp_c):\n", " return temp_c + 273.15\n", "\n", "print(f'freezing point of water in Kelvin: {celsius_to_kelvin(0.)}')" ], "cell_type": "code", "execution_count": null, "outputs": [ ], "metadata": { "attributes": { "classes": [ "" ], "id": "" } } }, { "id": "cell-6", "source": "\n\n\nThat’s a float! A
\n.
in a number makes it a float, rather than an integer.
What about converting Fahrenheit to Kelvin? We could write out both formulae, but we don’t need to. Instead, we can compose the two functions we have already created:
\n", "cell_type": "markdown", "metadata": { "editable": false, "collapsed": false } }, { "id": "cell-7", "source": [ "def fahr_to_kelvin(temp_f):\n", " temp_c = fahr_to_celsius(temp_f)\n", " temp_k = celsius_to_kelvin(temp_c)\n", " return temp_k\n", "\n", "print(f'boiling point of water in Kelvin: {fahr_to_kelvin(212.0)}')" ], "cell_type": "code", "execution_count": null, "outputs": [ ], "metadata": { "attributes": { "classes": [ "" ], "id": "" } } }, { "id": "cell-8", "source": "This is our first taste of how larger programs are built: we define basic operations, then combine them in ever-larger chunks to get the effect we want. Real-life functions will usually be larger than the ones shown here — typically half a dozen to a few dozen lines — but they shouldn’t ever be much longer than that, or the next person who reads it won’t be able to understand what’s going on.
\nDocumenting your code is extremely, extremely, extremely important to do. We all forget what we’re doing, it’s only normal, so documenting what you’re doing is key to being able to restart work later.
\n", "cell_type": "markdown", "metadata": { "editable": false, "collapsed": false } }, { "id": "cell-9", "source": [ "def fahr_to_kelvin(temp_f):\n", " \"\"\"\n", " Converts a temperature from Fahrenheit to Kelvin\n", "\n", " temp_f: the temperature in Fahrenheit\n", "\n", " returns the temperature in Celsius\n", " \"\"\"\n", " temp_c = fahr_to_celsius(temp_f)\n", " temp_k = celsius_to_kelvin(temp_c)\n", " return temp_k\n", "\n", "print(f'boiling point of water in Kelvin: {fahr_to_kelvin(212.0)}')" ], "cell_type": "code", "execution_count": null, "outputs": [ ], "metadata": { "attributes": { "classes": [ "" ], "id": "" } } }, { "id": "cell-10", "source": "For a function this small, with such a descriptive name (fahr_to_kelvin
) it feels quite obvious what the function should do, what inputs it takes, what outputs it produces. However, you will thank yourself in the future if you do this now. You may think you will remember what the code does, but, be kind to your future self who is busy and stressed and may not want to spend time reading the code over again to figure out what every single function does.
\n\n", "cell_type": "markdown", "metadata": { "editable": false, "collapsed": false } }, { "id": "cell-11", "source": [ "# Test out solutions here!\n", "def average2(a, b):\n", " c =\n", " return c\n", "\n", "print(average2(32326, 631))" ], "cell_type": "code", "execution_count": null, "outputs": [ ], "metadata": { "attributes": { "classes": [ "" ], "id": "" } } }, { "id": "cell-12", "source": "Question: Converting statements to functions\nA lot of what you’ll do in programing is to turn a procedure that you want to do, into statements and a function.
\nFill in the missing portions of this function, two average numbers. Then use it to find the average of 32326 and 631
\n\n👁 View solution
\n\n\ndef average2(a, b):\n c = (a + b) / 2\n return c\n
We call it “average2” here because it will only average two numbers. It will not work for three numbers or a list of them.
\n
\n\n", "cell_type": "markdown", "metadata": { "editable": false, "collapsed": false } }, { "id": "cell-13", "source": [ "# Test out solutions here!\n", "\n", "\n", "print(pythagorus(1234, 4321)) # Should return 4493.750883170984" ], "cell_type": "code", "execution_count": null, "outputs": [ ], "metadata": { "attributes": { "classes": [ "" ], "id": "" } } }, { "id": "cell-14", "source": "Question: A more complicated example\nThe formula for a 90° triangle can be expressed as: \\(c = \\sqrt{a^2 + b^2}\\)
\n\n
\n- Write a function which takes
\na
andb
, and calculatesc
- Name this function “pythagorus”
\n- Remember to import math, if you haven’t already
\n\n👁 View solution
\n\n\ndef pythagorus(a, b):\n c = math.sqrt(math.pow(a, 2) + math.pow(b, 2))\n return c\n
In composing our temperature conversion functions, we created variables inside of those functions, temp
, temp_c
, temp_f
, and temp_k
. We refer to these variables as local variables because they no longer exist once the function is done executing. If we try to access their values outside of the function, we will encounter an error:
If you want to reuse the temperature in Kelvin after you have calculated it with fahr_to_kelvin, you can store the result of the function call in a variable:
\n", "cell_type": "markdown", "metadata": { "editable": false, "collapsed": false } }, { "id": "cell-17", "source": [ "temp_kelvin = fahr_to_kelvin(212.0)\n", "print(f'temperature in Kelvin was: {temp_kelvin}')" ], "cell_type": "code", "execution_count": null, "outputs": [ ], "metadata": { "attributes": { "classes": [ "" ], "id": "" } } }, { "id": "cell-18", "source": "Watch out for scope issues:
\n\n\nQuestion: Scope\nGiven the above code, which variables are accessible at Locations 1, 2, and 3?
\n\n👁 View solution
\n\n\n
\n- a, b
\n- a and b are there but you shouldn’t use these. x, y, c, d, e are also accessible.
\n- a, b.
\n
If we usually want a function to work one way, but occasionally need it to do something else, we can allow people to pass a parameter when they need to but provide a default to make the normal case easier. The example below shows how Python matches values to parameters:
\n", "cell_type": "markdown", "metadata": { "editable": false, "collapsed": false } }, { "id": "cell-21", "source": [ "def display(a=1, b=2, c=3):\n", " print(f'a: {a}, b: {b}, c: {c}')\n", "\n", "# no parameters:\n", "display()\n", "# one parameter:\n", "display(55)\n", "# two parameters:\n", "display(55, 66)" ], "cell_type": "code", "execution_count": null, "outputs": [ ], "metadata": { "attributes": { "classes": [ "" ], "id": "" } } }, { "id": "cell-22", "source": "As this example shows, parameters are matched up from left to right, and any that haven’t been given a value explicitly get their default value. We can override this behavior by naming the value as we pass it in:
\n", "cell_type": "markdown", "metadata": { "editable": false, "collapsed": false } }, { "id": "cell-23", "source": [ "# only setting the value of c\n", "display(c=77)" ], "cell_type": "code", "execution_count": null, "outputs": [ ], "metadata": { "attributes": { "classes": [ "" ], "id": "" } } }, { "id": "cell-24", "source": "\n\n", "cell_type": "markdown", "metadata": { "editable": false, "collapsed": false } }, { "id": "cell-25", "source": [ "# Test things out here!\n", "def myFunction # Fix this function!\n", "\n", "\n", "# Here are some test cases, for you to check if your function works!\n", "myFunction('This is a message')\n", "myFunction('This is a message', signature='Jane Doe')" ], "cell_type": "code", "execution_count": null, "outputs": [ ], "metadata": { "attributes": { "classes": [ "" ], "id": "" } } }, { "id": "cell-26", "source": "\n", "cell_type": "markdown", "metadata": { "editable": false, "collapsed": false } }, { "cell_type": "markdown", "id": "final-ending-cell", "metadata": { "editable": false, "collapsed": false }, "source": [ "# Key Points\n\n", "- Functions are foundational in Python\n", "- Everything you do will require functions\n", "- Functions keep your code DRY (don't repeat yourself), reducing your copying and pasting or rewriting the same block of code.\n", "- Deciding what part of your code should, or should not be, a function is something that will come with practice.\n", "\n# Congratulations on successfully completing this tutorial!\n\n", "Please [fill out the feedback on the GTN website](https://training.galaxyproject.org/training-material/topics/data-science/tutorials/python-functions/tutorial.html#feedback) and check there for further resources!\n" ] } ] }Question: Exercise: Signing a message\nLet’s test out a default argument. Imagine you are printing out a message, and at the bottom it should have a signature.
\nInputs:
\n\n
\n- \n
message
: a variable that is always provided to the function, it has no default.- \n
signature
: a variable that can be optionally provided, it should have a default like your name.You can accomplish this with three print statements:
\n\n
\n- Print the message
\n- Print nothing (i.e.
\nprint()
)- Print a signature variable.
\n\n👁 View solution
\n\n\ndef myFunction(message, signature=\"Your name\"):\n print(message)\n print()\n print(signature)\n