{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Functions and modules\n", "=====================\n", "\n", "Introduction\n", "------------\n", "\n", "Functions allow us to group a number of statements into a logical block. We communicate with a function through a clearly defined interface, providing certain parameters to the function, and receiving some information back. Apart from this interface, we generally do not how exactly a function does the work to obtain the value it returns\n", "\n", "For example the function `math.sqrt`: we do not know how exactly it computes the square root, but we know about the interface: if we pass *x* into the function, it will return (an approximation of) $\\\\sqrt{x}$.\n", "\n", "This abstraction is a useful thing: it is a common technique in engineering to break down a system into smaller (black-box) components that all work together through well defined interfaces, but which do not need to know about the internal realisations of each other’s functionality. In fact, not having to care about these implementation details can help to have a clearer view of the system composed of many of these components.\n", "\n", "Functions provide the basic building blocks of functionality in larger programs (and computer simulations), and help to control the inherent complexity of the process.\n", "\n", "We can group functions together into a Python module (see [modules](#Modules)), and in this way create our own libraries of functionality.\n", "\n", "Using functions\n", "---------------\n", "\n", "The word “function” has different meanings in mathematics and programming. In programming it refers to a named sequence of operations that perform a computation. For example, the function `sqrt()` which is defined in the `math` module computes the square root of a given value:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "2.0" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from math import sqrt\n", "sqrt(4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The value we pass to the function `sqrt` is 4 in this example. This value is called the *argument* of the function. A function may have more than one argument.\n", "\n", "The function returns the value 2.0 (the result of its computation) to the “calling context”. This value is called the *return value* of the function.\n", "\n", "It is common to say that a function *takes* an argument and *returns* a result or return value.\n", "\n", "#### Common confusion about printing and returning values\n", "\n", "It is a common beginner’s mistake to confuse the *printing* of values with *returning* values. In the following example it is hard to see whether the function `math.sin` returns a value or whether it prints the value:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "0.9092974268256817" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import math\n", "math.sin(2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We import the `math` module, and call the `math.sin` function with an argument of `2`. The `math.sin(2)` call will actually *return* the value `0.909...` not print it. However, because we have not assigned the return value to a variable, the Python prompt will print the returned object.\n", "\n", "The following alternative sequence works only if the value is returned:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0.9092974268256817\n" ] } ], "source": [ "x = math.sin(2)\n", "print(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The return value of the function call `math.sin(2)` is assigned to the variable `x`, and `x` is printed in the next line.\n", "\n", "Generally, functions should execute “silently” (i.e. not print anything) and report the result of their computation through the return value.\n", "\n", "Part of the confusion about printed versus return values at the Python prompt comes from the Python prompt printing (a representation) of returned objects *if* the returned objects are not assigned. Generally, seeing the returned objects is exactly what we want (as we normally care about the returned object), just when learning Python this may cause mild confusion about functions returning values or printing values.\n", "\n", "##### Further information\n", "\n", "- Think Python has a gentle introduction to functions (on which the previous paragraph is based) in [chapter 3 (Functions)](http://www.greenteapress.com/thinkpython/html/book004.html) and [chapter 6 (Fruitful functions)](http://www.greenteapress.com/thinkpython/html/book007.html)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Defining functions\n", "------------------\n", "\n", "The generic format of a function definitions:\n", "\n", "```python\n", "def my_function(arg1, arg2, ..., argn):\n", " \"\"\"Optional docstring.\"\"\"\n", "\n", " # Implementation of the function\n", "\n", " return result # optional\n", "\n", "#this is not part of the function\n", "some_command\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Allen Downey’s terminology (in his book [Think Python](http://www.greenteapress.com/thinkpython/html/index.html)) of fruitful and fruitless functions distinguishes between functions that return a value, and those that do not return a value. The distinction refers to whether a function provides a return value (=fruitful) or whether the function does not explicitly return a value (=fruitless). If a functions does not make use of the `return` statement, we tend to say that the function returns nothing (whereas in reality in will always return the `None` object when it terminates – even if the `return` statement is missing).\n", "\n", "For example, the function `greeting` will print “Hello World” when called (and is fruitless as it does not return a value)." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def greeting():\n", " print(\"Hello World!\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we call that function:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hello World!\n" ] } ], "source": [ "greeting()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "it prints “Hello World” to stdout, as we would expect. If we assign the return value of the function to a variable `x`, we can inspect it subsequently:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hello World!\n" ] } ], "source": [ "x = greeting()" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "None\n" ] } ], "source": [ "print(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and find that the `greeting` function has indeed returned the `None` object.\n", "\n", "Another example for a function that does not return any value (that means there is no `return` keyword in the function) would be:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def printpluses(n): \n", " print(n * \"+\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Generally, functions that return values are more useful as these can be used to assemble code (maybe as another function) by combining them cleverly. Let’s look at some examples of functions that do return a value.\n", "\n", "Suppose we need to define a function that computes the square of a given variable. The function source could be:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def square(x):\n", " return x * x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The keyword `def` tells Python that we are *defining* a function at that point. The function takes one argument (`x`). The function returns `x*x` which is of course $x^2$. Here is the listing of a file that shows how the function can be defined and used: (note that the numbers on the left are line numbers and are not part of the program)" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "attributes": { "classes": [ "numberLines" ], "id": "" }, "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 * 0 = 0\n", "1 * 1 = 1\n", "2 * 2 = 4\n", "3 * 3 = 9\n", "4 * 4 = 16\n" ] } ], "source": [ "def square(x):\n", " return x * x\n", "\n", "for i in range(5):\n", " i_squared = square(i)\n", " print(i, '*', i, '=', i_squared)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is worth mentioning that lines 1 and 2 define the square function whereas lines 4 to 6 are the main program." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can define functions that take more than one argument:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": true }, "outputs": [], "source": [ "import math\n", "\n", "def hypot(x, y):\n", " return math.sqrt(x * x + y * y)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is also possible to return more than one argument. Here is an example of a function that converts a given string into all characters uppercase and all characters lowercase and returns the two versions. We have included the main program to show how this function can be called:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Banana in lowercase: banana and in uppercase BANANA\n" ] } ], "source": [ "def upperAndLower(string):\n", " return string.upper(), string.lower()\n", "\n", "testword = 'Banana'\n", "\n", "uppercase, lowercase = upperAndLower(testword)\n", "\n", "print(testword, 'in lowercase:', lowercase,\n", " 'and in uppercase', uppercase)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can define multiple Python functions in one file. Here is an example with two functions:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "*****************Hello world!*****************\n" ] } ], "source": [ "def returnstars( n ):\n", " return n * '*'\n", "\n", "def print_centred_in_stars( string ):\n", " linelength = 46 \n", " starstring = returnstars((linelength - len(string)) // 2)\n", "\n", " print(starstring + string + starstring)\n", "\n", "print_centred_in_stars('Hello world!')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Further reading\n", "\n", "- [Python Tutorial: Section 4.6 Defining Functions](http://docs.python.org/tutorial/controlflow.html#defining-functions)\n", "\n", "Default values and optional parameters\n", "--------------------------------------\n", "\n", "Python allows to define *default* values for function parameters. Here is an example: This program will print the following output when executed: So how does it work? The function `print_mult_table` takes two arguments: `n` and `upto`. The first argument `n` is a “normal” variable. The second argument `upto` has a default value of 10. In other words: should the user of this function only provide one argument, then this provides the value for `n` and `upto` will default to 10. If two arguments are provided, the first one will be for `n` and the second for `upto` (as shown in the code example above)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Modules\n", "-------\n", "\n", "Modules\n", "\n", "- Group together functionality\n", "\n", "- Provide namespaces\n", "\n", "- Python’s standard library contains a vast collection of modules - “Batteries Included”\n", "\n", " - Try `help(’modules’)`\n", "\n", "- Means of extending Python\n", "\n", "### Importing modules" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": true }, "outputs": [], "source": [ "import math" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This will introduce the name `math` into the namespace in which the import command was issued. The names within the `math` module will not appear in the enclosing namespace: they must be accessed through the name `math`. For example: `math.sin`." ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "collapsed": true }, "outputs": [], "source": [ "import math, cmath" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "More than one module can be imported in the same statement, although the [Python Style Guide](http://www.python.org/dev/peps/pep-0008/) recommends not to do this. Instead, we should write" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "collapsed": true }, "outputs": [], "source": [ "import math\n", "import cmath\n", "\n", "import math as mathematics" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The name by which the module is known locally can be different from its “official” name. Typical uses of this are\n", "\n", "- To avoid name clashes with existing names\n", "\n", "- To change the name to something more manageable. For example `import SimpleHTTPServer as shs`. This is discouraged for production code (as longer meaningful names make programs far more understandable than short cryptic ones), but for interactively testing out ideas, being able to use a short synonym can make your life much easier. Given that (imported) modules are first class objects, you can, of course, simply do `shs = SimpleHTTPServer` in order to obtain the more easily typable handle on the module.\n", "\n", "" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "collapsed": true }, "outputs": [], "source": [ "from math import sin" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This will import the `sin` function from the `math` module, but it will not introduce the name math into the current namespace. It will only introduce the name `sin` into the current namespace. It is possible to pull in more than one name from the module in one go:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "collapsed": true }, "outputs": [], "source": [ "from math import sin, cos" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, let’s look at this notation:" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "collapsed": true }, "outputs": [], "source": [ "from math import *" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Once again, this does not introduce the name math into the current namespace. It does however introduce *all public names* of the math module into the current namespace. Broadly speaking, it is a bad idea to do this:\n", "\n", "- Lots of new names will be dumped into the current namespace.\n", "\n", "- Are you sure they will not clobber any names already present?\n", "\n", "- It will be very difficult to trace where these names came from\n", "\n", "- Having said that, some modules (including ones in the standard library, recommend that they be imported in this way). Use with caution!\n", "\n", "- This is fine for interactive quick and dirty testing or small calculations." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Creating modules\n", "\n", "A module is in principle nothing else than a python file. Here is an example of a module file which is saved in `module1.py`:\n", "\n", "```python\n", "def someusefulfunction():\n", " pass\n", "\n", "print(\"My name is\", __name__)\n", "```\n", "\n", "We can execute this (module) file as a normal python program (for example `python module1.py`):" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "/Users/fangohr/hg/teaching-python/book/text/code\n" ] } ], "source": [ "cd code/" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "My name is __main__\r\n" ] } ], "source": [ "!python module1.py" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We note that the Python magic variable `__name__` takes the value `__main__` if the program file `module1.py` is executed.\n", "\n", "On the other hand, we can *import* `module1.py` in another file (which could have the name `prog.py`), for example like this:" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "My name is module1\n" ] } ], "source": [ "import module1 #in file prog.py" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When Python comes across the `import module1` statement in `prog.py`, it looks for the file `module1.py` in the current working directory (and if it can’t find it there in all the directories in `sys.path`) and opens the file `module1.py`. While parsing the file `module1.py` from top to bottom, it will add any function definitions in this file into the `module1` name space in the calling context (that is the main program in `prog.py`). It this example, there is only the function `someusefulfunction`. Once the import process is completed, we can make use of `module1.someusefulfunction` in `prog.py`. If Python comes across statements other than function (and class) definitions while importing `module1.py`, it carries those out immediately. In this case, it will thus come across the statement `print(My name is, __name__)`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note the difference to the output if we *import* `module1.py` rather than executing it on its own: `__name__` inside a module takes the value of the module name if the file is imported.\n", "\n", "### Use of \\_\\_name\\_\\_\n", "\n", "In summary,\n", "\n", "- `__name__` is `__main__` if the module file is run on its own\n", "\n", "- `__name__` is the name of the module (i.e. the module filename without the `.py` suffix) if the module file is imported.\n", "\n", "We can therefor use the following `if` statement in `module1.py` to write code that is *only run* when the module is executed on its own: This is useful to keep test programs or demonstrations of the abilities of a module in this “conditional” main program. It is common practice for any module files to have such a conditional main program which demonstrates its capabilities." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Example 1\n", "\n", "The next example shows a main program for the another file `vectools.py` that is used to demonstrate the capabilities of the functions defined in that file:\n", "\n", "```python\n", "from __future__ import division\n", "import math\n", "\n", "import numpy as N\n", "\n", "\n", "def norm(x):\n", " \"\"\"returns the magnitude of a vector x\"\"\"\n", " return math.sqrt(sum(x ** 2))\n", "\n", "\n", "def unitvector(x):\n", " \"\"\"returns a unit vector x/|x|. x needs to be a numpy array.\"\"\"\n", " xnorm = norm(x)\n", " if xnorm == 0:\n", " raise ValueError(\"Can't normalise vector with length 0\")\n", " return x / norm(x)\n", "\n", "\n", "if __name__ == \"__main__\":\n", " #a little demo of how the functions in this module can be used:\n", " x1 = N.array([0, 1, 2])\n", " print(\"The norm of \" + str(x1) + \" is \" + str(norm(x1)) + \".\")\n", " print(\"The unitvector in direction of \" + str(x1) + \" is \" \\\n", " + str(unitvector(x1)) + \".\")\n", "```\n", "\n", "If this file is executed using `python vectools.py`, then `__name__==__main__` is true, and the output reads" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The norm of [0 1 2] is 2.23606797749979.\r\n", "The unitvector in direction of [0 1 2] is [ 0. 0.4472136 0.89442719].\r\n" ] } ], "source": [ "!python3 vectortoolswithconditionalmain.py" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If this file is imported (i.e. used as a module) into another python file, then `__name__==__main__` is false, and that statement block will not be executed (and no output produced).\n", "\n", "This is quite a common way to conditionally execute code in files providing library-like functions. The code that is executed if the file is run on its own, often consists of a series of tests (to check that the file’s functions carry out the right operations – *regression tests* or *unit tests* ), or some examples of how the library functions in the file can be used.\n", "\n", "### Example 2\n", "\n", "Even if a Python program is not intended to be used as a module file, it is good practice to always use a conditional main program:\n", "\n", "- often, it turns out later that functions in the file can be reused (and saves work then)\n", "\n", "- this is convenient for regression testing.\n", "\n", "Suppose an exercise is given to write a function that returns the first 5 prime numbers, and in addition to print them. (There is of course a trivial solution to this as we know the prime numbers, and we should imagine that the required calculation is more complex). One might be tempted to write" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2 3 5 7 11 " ] } ], "source": [ "def primes5():\n", " return (2, 3, 5, 7, 11)\n", "\n", "for p in primes5():\n", " print(\"%d\" % p, end=' ')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is better style to use a conditional main function, i.e.:" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2 3 5 7 11 " ] } ], "source": [ "def primes5():\n", " return (2, 3, 5, 7, 11)\n", "\n", "if __name__==\"__main__\":\n", " for p in primes5():\n", " print(\"%d\" % p, end=' ')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A purist might argue that the following is even cleaner:" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2 3 5 7 11 " ] } ], "source": [ "def primes5():\n", " return (2, 3, 5, 7, 11)\n", "\n", "def main():\n", " for p in primes5():\n", " print(\"%d\" % p, end=' ')\n", "\n", "if __name__==\"__main__\":\n", " main()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "but either of the last two options is good.\n", "\n", "The example in [Many ways to compute a series](#Many-ways-to-compute-a-series) demonstrates this technique. Including functions with names starting with `test_` is compatible with the very useful py.test regression testing framework (see ).\n", "\n", "#### Further Reading\n", "\n", "- [Python Tutorial Section 6](http://docs.python.org/tutorial/modules.html#modules)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.4.4" } }, "nbformat": 4, "nbformat_minor": 1 }