{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "*This notebook contains an excerpt from the [Whirlwind Tour of Python](http://www.oreilly.com/programming/free/a-whirlwind-tour-of-python.csp) by Jake VanderPlas; the content is available [on GitHub](https://github.com/jakevdp/WhirlwindTourOfPython).*\n", "\n", "*The text and code are released under the [CC0](https://github.com/jakevdp/WhirlwindTourOfPython/blob/master/LICENSE) license; see also the companion project, the [Python Data Science Handbook](https://github.com/jakevdp/PythonDataScienceHandbook).*\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "< [Control Flow](07-Control-Flow-Statements.ipynb) | [Contents](Index.ipynb) | [Errors and Exceptions](09-Errors-and-Exceptions.ipynb) >" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Defining and Using 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." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Using Functions\n", "\n", "Functions are groups of code that have a name, and can be called using parentheses.\n", "We've seen functions before. For example, ``print`` in Python 3 is a function:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "abc\n" ] } ], "source": [ "print('abc')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here ``print`` is the function name, and ``'abc'`` is the function's *argument*.\n", "\n", "In addition to arguments, there are *keyword arguments* that are specified by name.\n", "One available keyword argument for the ``print()`` function (in Python 3) is ``sep``, which tells what character or characters should be used to separate multiple items:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 2 3\n" ] } ], "source": [ "print(1, 2, 3)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1--2--3\n" ] } ], "source": [ "print(1, 2, 3, sep='--')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When non-keyword arguments are used together with keyword arguments, the keyword arguments must come at the end." ] }, { "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.\n", "For example, we can encapsulate a version of our Fibonacci sequence code from the previous section as follows:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def fibonacci(N):\n", " L = []\n", " a, b = 0, 1\n", " while len(L) < N:\n", " a, b = b, a + b\n", " L.append(a)\n", " return L" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we have a function named ``fibonacci`` which takes a single argument ``N``, does something with this argument, and ``return``s a value; in this case, a list of the first ``N`` Fibonacci numbers:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fibonacci(10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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": 6, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3.0 4.0 (3-4j)\n" ] } ], "source": [ "def real_imag_conj(val):\n", " return val.real, val.imag, val.conjugate()\n", "\n", "r, i, c = real_imag_conj(3 + 4j)\n", "print(r, i, c)" ] }, { "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:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [], "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" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With a single argument, the result of the function call is identical to before:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fibonacci(10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But now we can use the function to explore new things, such as the effect of new starting values:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "[2, 2, 4, 6, 10, 16, 26, 42, 68, 110]" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fibonacci(10, 0, 2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The values can also be specified by name if desired, in which case the order of the named values does not matter:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "[3, 4, 7, 11, 18, 29, 47, 76, 123, 199]" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fibonacci(10, b=3, a=1)" ] }, { "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": 11, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def catch_all(*args, **kwargs):\n", " print(\"args =\", args)\n", " print(\"kwargs = \", kwargs)" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "args = (1, 2, 3)\n", "kwargs = {'a': 4, 'b': 5}\n" ] } ], "source": [ "catch_all(1, 2, 3, a=4, b=5)" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false }, "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": 14, "metadata": { "collapsed": false }, "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": [ "## Anonymous (``lambda``) Functions\n", "Earlier we quickly covered the most common way of defining functions, the ``def`` statement.\n", "You'll likely come across another way of defining short, one-off functions with the ``lambda`` statement.\n", "It looks something like this:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "3" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "add = lambda x, y: x + y\n", "add(1, 2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This lambda function is roughly equivalent to" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def add(x, y):\n", " return x + y" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So why would you ever want to use such a thing?\n", "Primarily, it comes down to the fact that *everything is an object* in Python, even functions themselves!\n", "That means that functions can be passed as arguments to functions.\n", "\n", "As an example of this, suppose we have some data stored in a list of dictionaries:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "collapsed": false }, "outputs": [], "source": [ "data = [{'first':'Guido', 'last':'Van Rossum', 'YOB':1956},\n", " {'first':'Grace', 'last':'Hopper', 'YOB':1906},\n", " {'first':'Alan', 'last':'Turing', 'YOB':1912}]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now suppose we want to sort this data.\n", "Python has a ``sorted`` function that does this:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "[1, 2, 3, 4, 5, 6]" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sorted([2,4,3,5,1,6])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But dictionaries are not orderable: we need a way to tell the function *how* to sort our data.\n", "We can do this by specifying the ``key`` function, a function which given an item returns the sorting key for that item:" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "[{'YOB': 1912, 'first': 'Alan', 'last': 'Turing'},\n", " {'YOB': 1906, 'first': 'Grace', 'last': 'Hopper'},\n", " {'YOB': 1956, 'first': 'Guido', 'last': 'Van Rossum'}]" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# sort alphabetically by first name\n", "sorted(data, key=lambda item: item['first'])" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "[{'YOB': 1906, 'first': 'Grace', 'last': 'Hopper'},\n", " {'YOB': 1912, 'first': 'Alan', 'last': 'Turing'},\n", " {'YOB': 1956, 'first': 'Guido', 'last': 'Van Rossum'}]" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# sort by year of birth\n", "sorted(data, key=lambda item: item['YOB'])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "While these key functions could certainly be created by the normal, ``def`` syntax, the ``lambda`` syntax is convenient for such short one-off functions like these." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "< [Control Flow](07-Control-Flow-Statements.ipynb) | [Contents](Index.ipynb) | [Errors and Exceptions](09-Errors-and-Exceptions.ipynb) >" ] } ], "metadata": { "anaconda-cloud": {}, "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.5.1" } }, "nbformat": 4, "nbformat_minor": 0 }