{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from __future__ import print_function" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# functions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So far, our scripts have been simple, single-use code blocks.\n", "One way to organize our Python code and to make it more readable and reusable is to factor-out useful pieces into reusable *functions*.\n", "Here we'll cover two ways of creating functions: the ``def`` statement, useful for any type of function, and the ``lambda`` statement, useful for creating short anonymous functions.\n", "\n", "Functions are used to organize program flow, especially to allow us to easily do commonly needed tasks over and over again. We've already used a lot of functions, such as those that work on lists (`append()` and `pop()`) or strings (like `replace()`). Here we see how to write our own functions. If you are familiar will matlab you will find functions very similar." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A function takes arguments, listed in the `()` and returns a value. Even if you don't explictly give a return value, one will be return (e.g., `None`). \n", "\n", "Here's a simple example of a function that takes a single argument, `i`" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hello\n" ] } ], "source": [ "a = print(\"Hello\")\n", "a\n", "# print(type(a))\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Defining Functions\n", "Functions become even more useful when we begin to define our own, organizing functionality to be used in multiple places.\n", "In Python, functions are defined with the ``def`` statement." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "in the function, i = 10\n", "in the function, i = 5\n", "Help on function my_fun in module __main__:\n", "\n", "my_fun(i)\n", " input:\n", " i integer\n", " output:\n", " None\n", " Use:\n", "\n", "in the function, i = 10\n" ] } ], "source": [ "def my_fun(i):\n", " \"\"\" input:\n", " i integer\n", " output:\n", " None\n", " Use: \"\"\"\n", " print(\"in the function, i = {}\".format(i))\n", " \n", "my_fun(10)\n", "my_fun(5)\n", "help(my_fun)\n", "a = my_fun(10)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a = my_fun(0)\n", "print(a)\n", "print(type(a))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "functions are one place where _scope_ comes into play. A function has its own _namespace_. If a variable is not defined in that function, then it will look to the namespace from where it was called to see if that variable exists there. \n", "\n", "However, you should avoid this as much as possible (variables that persist across namespaces are called global variables).\n", "\n", "We already saw one instance of namespaces when we imported from the `math` module." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "-----\n", "----------\n" ] } ], "source": [ "global_var = 10\n", "\n", "def print_fun(string, n):\n", " \"\"\" \"\"\"\n", " if n < global_var:\n", " print(string*n)\n", " else:\n", " print(string*global_var)\n", "\n", "print_fun(\"-\", 5)\n", "print_fun(\"-\", 20)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "global_var = 100" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "--------------------------------------------------\n" ] } ], "source": [ "print_fun(\"-\",50)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "By default, python will let you read from a global, but not update it." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "in function outer = -100.0\n", "outside, outer = 1.0\n" ] } ], "source": [ "outer = 1.0\n", "\n", "def update():\n", " # uncomment this to allow us to access outer in the calling namespace\n", " # global outer\n", " outer = -100.0\n", " print(\"in function outer = {}\".format(outer))\n", " \n", "update()\n", "print(\"outside, outer = {}\".format(outer))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "functions always return a value—if one is not explicitly given, then they return None, otherwise, they can return values (even multiple values) of any type" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "in the function, i = 10\n", "None\n" ] } ], "source": [ "a = my_fun(10)\n", "print(a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here's a simple function that takes two numbers and returns their product." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "12\n" ] } ], "source": [ "def multiply(a, b):\n", " return a*b\n", "\n", "c = multiply(3, 4)\n", "print(c)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Quick Exercise:

\n", "\n", "Write a simple function that takes a sentence (as a string) and returns an integer equal to the length of the longest word in the sentence. The `len()` function and the `.split()` methods will be useful here.\n", "\n", "
" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on function long_word in module __main__:\n", "\n", "long_word(sentence)\n", " input: sentence--> a string of text with many words\n", " output: count--> lenght of the longest word in sentence\n", "\n" ] } ], "source": [ "def long_word(sentence):\n", " \"\"\" input: sentence--> a string of text with many words\n", " output: count--> lenght of the longest word in sentence\"\"\"\n", " count = max([len(w) for w in sentence.split()])\n", " return(count)\n", "long_word('input: sentence--> a string of text with many words')\n", "help(long_word)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "None is a special quantity in python (analogous to `null` in some other languages). We can test on `None`—the preferred manner is to use `is`:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "we didn't do anything\n" ] } ], "source": [ "def do_nothing():\n", " pass\n", "\n", "a = do_nothing()\n", "if a is None:\n", " print(\"we didn't do anything\")" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a is None" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## More Complex Functions\n", "\n", "Here's a more complex example. We return a pair of variables—behind the scenes in python this is done by packing them into a tuple and then unpacking on the calling end. Also note the _docstring_ here." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "14\n", "[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233]\n" ] } ], "source": [ "def fib2(n): # return Fibonacci series up to n (from the python tutorial)\n", " \"\"\"input: n, max number in series\n", " output: l[], len(l) list and its length \n", " containing the Fibonacci series up to n. \"\"\"\n", " result = []\n", " a, b = 0, 1\n", " while a < n:\n", " result.append(a) # see below\n", " a, b = b, a+b\n", " return result, len(result)\n", "\n", "fib, n = fib2(250)\n", "print(n)\n", "print(fib)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that this function includes a docstring (just after the function definition). This is used by the help system" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on function fib2 in module __main__:\n", "\n", "fib2(n)\n", " input: n, max number in series\n", " output: l[], len(l) list and its length \n", " containing the Fibonacci series up to n.\n", "\n" ] } ], "source": [ "help(fib2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Output types\n", "If you're familiar with strongly-typed languages like ``C``, you'll immediately notice that there is no type information associated with the function inputs or outputs.\n", "Python functions can return any Python object, simple or compound, which means constructs that may be difficult in other languages are straightforward in Python.\n", "\n", "For example, multiple return values are simply put in a tuple, which is indicated by commas:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def real_imag_conj(val):\n", " \"\"\" input: val Complex number (can also be real)\n", " Output: a,b,c: real, imaginary and complex conjugate of val\"\"\"\n", " return val.real, val.imag, val.conjugate()\n", "\n", "r, i, c = real_imag_conj(3 + 4j)\n", "print(r, i, c)\n", "help(real_imag_conj)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Default Argument Values\n", "\n", "Often when defining a function, there are certain values that we want the function to use *most* of the time, but we'd also like to give the user some flexibility.\n", "In this case, we can use *default values* for arguments.\n", "Consider the ``fibonacci`` function from before.\n", "What if we would like the user to be able to play with the starting values?\n", "We could do that as follows:\n" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 433494437, 701408733, 1134903170, 1836311903, 2971215073, 4807526976, 7778742049, 12586269025, 20365011074, 32951280099, 53316291173, 86267571272, 139583862445, 225851433717, 365435296162, 591286729879, 956722026041, 1548008755920, 2504730781961, 4052739537881, 6557470319842, 10610209857723, 17167680177565, 27777890035288, 44945570212853, 72723460248141, 117669030460994, 190392490709135, 308061521170129, 498454011879264, 806515533049393, 1304969544928657, 2111485077978050, 3416454622906707, 5527939700884757, 8944394323791464, 14472334024676221, 23416728348467685, 37889062373143906, 61305790721611591, 99194853094755497, 160500643816367088, 259695496911122585, 420196140727489673, 679891637638612258, 1100087778366101931, 1779979416004714189, 2880067194370816120, 4660046610375530309, 7540113804746346429, 12200160415121876738, 19740274219868223167, 31940434634990099905, 51680708854858323072, 83621143489848422977, 135301852344706746049, 218922995834555169026, 354224848179261915075]\n" ] } ], "source": [ "def fibonacci(N, a=0, b=1):\n", " L = []\n", " while len(L) < N:\n", " a, b = b, a + b\n", " L.append(a)\n", " return L\n", "\n", "print(fibonacci(100))\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "it is important to note that python evaluates the optional arguments once—when the function is defined. This means that if you make the default an empty object, for instance, it will persist across all calls.\n", "\n", "**This leads to one of the most common errors for beginners**\n", "\n", "Here's an example of trying to initialize to an empty list:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1]\n", "[1, 2]\n", "[1, 2, 3]\n" ] } ], "source": [ "def f(a, L=[]):\n", " L.append(a)\n", " return L\n", "\n", "print(f(1))\n", "print(f(2))\n", "print(f(3))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice that each call does not create its own separate list. Instead a single empty list was created when the function was first processed, and this list persists in memory as the default value for the optional argument `L`. \n", "\n", "If we want a unique list created each time (e.g., a separate place in memory), we instead initialize the argument's value to `None` and then check its actual value and create an empty list in the function body itself if the default value was unchanged." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1]\n", "[2]\n", "[3]\n" ] } ], "source": [ "def fnew(a, L=None):\n", " if L is None:\n", " L = []\n", " L.append(a)\n", " return L\n", "\n", "print(fnew(1))\n", "print(fnew(2))\n", "print(fnew(3))" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 2]\n" ] } ], "source": [ "L = fnew(1)\n", "print(fnew(2, L=L))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice that the same `None` that we saw previously comes into play here. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "L" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## ``*args`` and ``**kwargs``: Flexible Arguments\n", "Sometimes you might wish to write a function in which you don't initially know how many arguments the user will pass.\n", "In this case, you can use the special form ``*args`` and ``**kwargs`` to catch all arguments that are passed.\n", "Here is an example:" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [], "source": [ "def catch_all(*args, **kwargs):\n", " print(\"args =\", args)\n", " print(\"kwargs = \", kwargs)" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "args = ([1, 2, 2],)\n", "kwargs = {'a': 4, 'b': 5}\n" ] } ], "source": [ "L = [1,2,2]\n", "catch_all(L, a=4, b=5)" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "args = ('a',)\n", "kwargs = {'keyword': 2}\n" ] } ], "source": [ "catch_all('a', keyword=2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here it is not the names ``args`` and ``kwargs`` that are important, but the ``*`` characters preceding them.\n", "``args`` and ``kwargs`` are just the variable names often used by convention, short for \"arguments\" and \"keyword arguments\".\n", "The operative difference is the asterisk characters: a single ``*`` before a variable means \"expand this as a sequence\", while a double ``**`` before a variable means \"expand this as a dictionary\".\n", "In fact, this syntax can be used not only with the function definition, but with the function call as well!" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "args = (1, 2, 3)\n", "kwargs = {'pi': 3.14}\n" ] } ], "source": [ "inputs = (1, 2, 3)\n", "keywords = {'pi': 3.14}\n", "\n", "catch_all(*inputs, **keywords)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Lambdas" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Lambdas are \"disposible\" functions. These are small, nameless functions that are often used as arguments in other functions.\n", "\n", "Ex, from the official tutorial: we have a list of tuples. We want to sort the list based on the second item in the tuple. The `sort` method can take a `key` optional argument that tells us how to interpret the list item for sorting" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four'), (5,'afive')]\n", "set_pairs = set(pairs)\n", "print(set_pairs)\n", "\n", "pairs.sort(key=lambda p: p[1])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "pairs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here we use a lambda in an extract from a list (with the filter command)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "squares = [x**2 for x in range(100)]\n", "sq = list(filter(lambda x : x%2 == 0 and x%3 == 0, squares))\n", "sq" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "help(filter)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "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.7.3" } }, "nbformat": 4, "nbformat_minor": 1 }