{ "metadata": { "name": "more_functions" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "More Functions\n", "===\n", "Earlier we learned the most bare-boned versions of functions. In this section we will learn more general concepts about functions, such as how to use functions to return values, and how to pass different kinds of data structures between functions." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[Previous: Dictionaries](http://nbviewer.ipython.org/urls/raw.github.com/ehmatthes/intro_programming/master/notebooks/dictionaries.ipynb) | \n", "[Home](http://nbviewer.ipython.org/urls/raw.github.com/ehmatthes/intro_programming/master/notebooks/index.ipynb) |\n", "[Next: Classes](http://nbviewer.ipython.org/urls/raw.github.com/ehmatthes/intro_programming/master/notebooks/classes.ipynb) | " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Contents\n", "===\n", "- [Default argument values](#default_values)\n", "- [Positional arguments](#positional_arguments)\n", "- [Keyword arguments](#keyword_arguments)\n", " - [Mixing positional and keyword arguments](#positional_and_keyword)\n", "- [Accepting an arbitrary number of arguments](#arbitrary_arguments)\n", " - [Accepting a sequence of arbitrary length](#arbitrary_sequence)\n", " - [Accepting an arbitrary number of keyword arguments](#arbitrary_keyword_arguments)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Default argument values\n", "===\n", "When we first introduced functions, we started with this example:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def thank_you(name):\n", " # This function prints a two-line personalized thank you message.\n", " print(\"\\nYou are doing good work, %s!\" % name)\n", " print(\"Thank you very much for your efforts on this project.\")\n", " \n", "thank_you('Adriana')\n", "thank_you('Billy')\n", "thank_you('Caroline')" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "\n", "You are doing good work, Adriana!\n", "Thank you very much for your efforts on this project.\n", "\n", "You are doing good work, Billy!\n", "Thank you very much for your efforts on this project.\n", "\n", "You are doing good work, Caroline!\n", "Thank you very much for your efforts on this project.\n" ] } ], "prompt_number": 1 }, { "cell_type": "markdown", "metadata": {}, "source": [ "This function works fine, but it fails if you don't pass in a value:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def thank_you(name):\n", " # This function prints a two-line personalized thank you message.\n", " print(\"\\nYou are doing good work, %s!\" % name)\n", " print(\"Thank you very much for your efforts on this project.\")\n", " \n", "thank_you('Adriana')\n", "thank_you('Billy')\n", "thank_you('Caroline')\n", "thank_you()" ], "language": "python", "metadata": {}, "outputs": [ { "ename": "TypeError", "evalue": "thank_you() takes exactly 1 argument (0 given)", "output_type": "pyerr", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0mthank_you\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Billy'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 8\u001b[0m \u001b[0mthank_you\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Caroline'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 9\u001b[0;31m \u001b[0mthank_you\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: thank_you() takes exactly 1 argument (0 given)" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\n", "You are doing good work, Adriana!\n", "Thank you very much for your efforts on this project.\n", "\n", "You are doing good work, Billy!\n", "Thank you very much for your efforts on this project.\n", "\n", "You are doing good work, Caroline!\n", "Thank you very much for your efforts on this project.\n" ] } ], "prompt_number": 2 }, { "cell_type": "markdown", "metadata": {}, "source": [ "That makes sense; the function needs to have a name in order to do its work, so without a name it is stuck.\n", "\n", "If you want your function to do something by default, even if no information is passed to it, you can do so by giving your arguments default values. You do this by specifying the default values when you define the function:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def thank_you(name='everyone'):\n", " # This function prints a two-line personalized thank you message.\n", " # If no name is passed in, it prints a general thank you message\n", " # to everyone.\n", " print(\"\\nYou are doing good work, %s!\" % name)\n", " print(\"Thank you very much for your efforts on this project.\")\n", " \n", "thank_you('Adriana')\n", "thank_you('Billy')\n", "thank_you('Caroline')\n", "thank_you()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "\n", "You are doing good work, Adriana!\n", "Thank you very much for your efforts on this project.\n", "\n", "You are doing good work, Billy!\n", "Thank you very much for your efforts on this project.\n", "\n", "You are doing good work, Caroline!\n", "Thank you very much for your efforts on this project.\n", "\n", "You are doing good work, everyone!\n", "Thank you very much for your efforts on this project.\n" ] } ], "prompt_number": 5 }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is particularly useful when you have a number of arguments in your function, and some of those arguments almost always have the same value. This allows people who use the function to only specify the values that are unique to their use of the function." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[top](#)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Positional Arguments\n", "===\n", "Much of what you will have to learn about using functions involves how to pass values from your calling statement to the function itself. The example we just looked at is pretty simple, in that the function only needed one argument in order to do its work. Let's take a look at a function that requires two arguments to do its work.\n", "\n", "Let's make a simple function that takes in three arguments. Let's make a function that takes in a person's first and last name, and then prints out everything it knows about the person.\n", "\n", "Here is a simple implementation of this function:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def describe_person(first_name, last_name, age):\n", " # This function takes in a person's first and last name,\n", " # and their age.\n", " # It then prints this information out in a simple format.\n", " print(\"First name: %s\" % first_name.title())\n", " print(\"Last name: %s\" % last_name.title())\n", " print(\"Age: %d\\n\" % age)\n", "\n", "describe_person('brian', 'kernighan', 71)\n", "describe_person('ken', 'thompson', 70)\n", "describe_person('adele', 'goldberg', 68)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "First name: Brian\n", "Last name: Kernighan\n", "Age: 71\n", "\n", "First name: Ken\n", "Last name: Thompson\n", "Age: 70\n", "\n", "First name: Adele\n", "Last name: Goldberg\n", "Age: 68\n", "\n" ] } ], "prompt_number": 22 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The arguments in this function are `first_name`, `last_name`, and `age`. These are called *positional arguments* because Python knows which value to assign to each by the order in which you give the function values. In the calling line\n", "\n", " describe_person('brian', 'kernighan', 71)\n", "\n", "we send the values *brian*, *kernighan*, and *71* to the function. Python matches the first value *brian* with the first argument `first_name`. It matches the second value *kernighan* with the second argument `last_name`. Finally it matches the third value *71* with the third argument `age`.\n", "\n", "This is pretty straightforward, but it means we have to make sure to get the arguments in the right order. If we mess up the order, we get nonsense results or an error:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def describe_person(first_name, last_name, age):\n", " # This function takes in a person's first and last name,\n", " # and their age.\n", " # It then prints this information out in a simple format.\n", " print(\"First name: %s\" % first_name.title())\n", " print(\"Last name: %s\" % last_name.title())\n", " print(\"Age: %d\\n\" % age)\n", "\n", "describe_person(71, 'brian', 'kernighan')\n", "describe_person(70, 'ken', 'thompson')\n", "describe_person(68, 'adele', 'goldberg')" ], "language": "python", "metadata": {}, "outputs": [ { "ename": "AttributeError", "evalue": "'int' object has no attribute 'title'", "output_type": "pyerr", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0;32mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Age: %d\\n\"\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0mage\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 8\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 9\u001b[0;31m \u001b[0mdescribe_person\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m71\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'brian'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'kernighan'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 10\u001b[0m \u001b[0mdescribe_person\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m70\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'ken'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'thompson'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 11\u001b[0m \u001b[0mdescribe_person\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m68\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'adele'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'goldberg'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m\u001b[0m in \u001b[0;36mdescribe_person\u001b[0;34m(first_name, last_name, age)\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;31m# and their age.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0;31m# It then prints this information out in a simple format.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 5\u001b[0;31m \u001b[0;32mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"First name: %s\"\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0mfirst_name\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtitle\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 6\u001b[0m \u001b[0;32mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Last name: %s\"\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0mlast_name\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtitle\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0;32mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Age: %d\\n\"\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0mage\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mAttributeError\u001b[0m: 'int' object has no attribute 'title'" ] } ], "prompt_number": 23 }, { "cell_type": "markdown", "metadata": {}, "source": [ "This fails because Python tries to match the value 71 with the argument `first_name`, the value *brian* with the argument `last_name`, and the value *kernighan* with the argument `age`. Then when it tries to print the value `first_name.title()`, it realizes it can't use the `title()` method on an integer." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[top](#)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Keyword arguments\n", "===\n", "Python allows us to use a syntax called *keyword arguments*. In this case, we can give the arguments in any order when we call the function, as long as we use the name of the arguments in our calling statement. Here is how the previous code can be made to work using keyword arguments:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def describe_person(first_name, last_name, age):\n", " # This function takes in a person's first and last name,\n", " # and their age.\n", " # It then prints this information out in a simple format.\n", " print(\"First name: %s\" % first_name.title())\n", " print(\"Last name: %s\" % last_name.title())\n", " print(\"Age: %d\\n\" % age)\n", "\n", "describe_person(age=71, first_name='brian', last_name='kernighan')\n", "describe_person(age=70, first_name='ken', last_name='thompson')\n", "describe_person(age=68, first_name='adele', last_name='goldberg')" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "First name: Brian\n", "Last name: Kernighan\n", "Age: 71\n", "\n", "First name: Ken\n", "Last name: Thompson\n", "Age: 70\n", "\n", "First name: Adele\n", "Last name: Goldberg\n", "Age: 68\n", "\n" ] } ], "prompt_number": 24 }, { "cell_type": "markdown", "metadata": {}, "source": [ "This works, because Python does not have to match values to arguments by position. It matches the value 71 with the argument `age`, because the value 71 is clearly marked to go with that argument. This syntax is a little more typing, but it makes for very readable code." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Mixing positional and keyword arguments\n", "---\n", "It can make good sense sometimes to mix positional and keyword arguments. In our previous example, we can expect this function to always take in a first name and a last name. Before we start mixing positional and keyword arguments, let's add another piece of information to our description of a person. Let's also go back to using just positional arguments for a moment:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def describe_person(first_name, last_name, age, favorite_language):\n", " # This function takes in a person's first and last name,\n", " # their age, and their favorite language.\n", " # It then prints this information out in a simple format.\n", " print(\"First name: %s\" % first_name.title())\n", " print(\"Last name: %s\" % last_name.title())\n", " print(\"Age: %d\" % age)\n", " print(\"Favorite language: %s\\n\" % favorite_language)\n", "\n", "describe_person('brian', 'kernighan', 71, 'C')\n", "describe_person('ken', 'thompson', 70, 'Go')\n", "describe_person('adele', 'goldberg', 68, 'Smalltalk')" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "First name: Brian\n", "Last name: Kernighan\n", "Age: 71\n", "Favorite language: C\n", "\n", "First name: Ken\n", "Last name: Thompson\n", "Age: 70\n", "Favorite language: Go\n", "\n", "First name: Adele\n", "Last name: Goldberg\n", "Age: 68\n", "Favorite language: Smalltalk\n", "\n" ] } ], "prompt_number": 25 }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can expect anyone who uses this function to supply a first name and a last name, in that order. But now we are starting to include some information that might not apply to everyone. We can address this by keeping positional arguments for the first name and last name, but expect keyword arguments for everything else. We can show this works by adding a few more people, and having different information about each person:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def describe_person(first_name, last_name, age=None, favorite_language=None, died=None):\n", " # This function takes in a person's first and last name,\n", " # their age, and their favorite language.\n", " # It then prints this information out in a simple format.\n", " \n", " # Required information:\n", " print(\"First name: %s\" % first_name.title())\n", " print(\"Last name: %s\" % last_name.title())\n", " \n", " # Optional information:\n", " if age:\n", " print(\"Age: %d\" % age)\n", " if favorite_language:\n", " print(\"Favorite language: %s\" % favorite_language)\n", " if died:\n", " print(\"Died: %d\" % died)\n", " \n", " # Blank line at end.\n", " print(\"\\n\")\n", "\n", "describe_person('brian', 'kernighan', favorite_language='C')\n", "describe_person('ken', 'thompson', age=70)\n", "describe_person('adele', 'goldberg', age=68, favorite_language='Smalltalk')\n", "describe_person('dennis', 'ritchie', favorite_language='C', died=2011)\n", "describe_person('guido', 'van rossum', favorite_language='Python')" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "First name: Brian\n", "Last name: Kernighan\n", "Favorite language: C\n", "\n", "\n", "First name: Ken\n", "Last name: Thompson\n", "Age: 70\n", "\n", "\n", "First name: Adele\n", "Last name: Goldberg\n", "Age: 68\n", "Favorite language: Smalltalk\n", "\n", "\n", "First name: Dennis\n", "Last name: Ritchie\n", "Favorite language: C\n", "Died: 2011\n", "\n", "\n", "First name: Guido\n", "Last name: Van Rossum\n", "Favorite language: Python\n", "\n", "\n" ] } ], "prompt_number": 27 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Everyone needs a first and last name, but everthing else is optional. This code takes advantage of the Python keyword `None`, which acts as an empty value for a variable. This way, the user is free to supply any of the 'extra' values they care to. Any arguments that don't receive a value are not displayed. Python matches these extra values by name, rather than by position. This is a very common and useful way to define functions." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[top](#)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Accepting an arbitrary number of arguments\n", "===\n", "We have now seen that using keyword arguments can allow for much more flexible calling statements.\n", "\n", "- This benefits you in your own programs, because you can write one function that can handle many different situations you might encounter.\n", "- This benefits you if other programmers use your programs, because your functions can apply to a wide range of situations.\n", "- This benefits you when you use other programmers' functions, because their functions can apply to many situations you will care about.\n", "\n", "There is another issue that we can address, though. Let's consider a function that takes two number in, and prints out the sum of the two numbers:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def adder(num_1, num_2):\n", " # This function adds two numbers together, and prints the sum.\n", " sum = num_1 + num_2\n", " print(\"The sum of your numbers is %d.\" % sum)\n", " \n", "# Let's add some numbers.\n", "adder(1, 2)\n", "adder(-1, 2)\n", "adder(1, -2)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "The sum of your numbers is 3.\n", "The sum of your numbers is 1.\n", "The sum of your numbers is -1.\n" ] } ], "prompt_number": 39 }, { "cell_type": "markdown", "metadata": {}, "source": [ "This function appears to work well. But what if we pass it three numbers, which is a perfectly reasonable thing to do mathematically?" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def adder(num_1, num_2):\n", " # This function adds two numbers together, and prints the sum.\n", " sum = num_1 + num_2\n", " print(\"The sum of your numbers is %d.\" % sum)\n", " \n", "# Let's add some numbers.\n", "adder(1, 2, 3)" ], "language": "python", "metadata": {}, "outputs": [ { "ename": "TypeError", "evalue": "adder() takes exactly 2 arguments (3 given)", "output_type": "pyerr", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0;31m# Let's add some numbers.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 7\u001b[0;31m \u001b[0madder\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m3\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: adder() takes exactly 2 arguments (3 given)" ] } ], "prompt_number": 40 }, { "cell_type": "markdown", "metadata": {}, "source": [ "This function fails, because no matter what mix of positional and keyword arguments we use, the function is only written two accept two arguments. In fact, a function written in this way will only work with *exactly* two arguments." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Accepting a sequence of arbitrary length\n", "---\n", "Python gives us a syntax for letting a function accept an arbitrary number of arguments. If we place an argument at the end of the list of arguments, with an asterisk in front of it, that argument will collect any remaining values from the calling statement into a tuple. Here is an example demonstrating how this works:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def example_function(arg_1, arg_2, *arg_3):\n", " # Let's look at the argument values.\n", " print('\\narg_1:', arg_1)\n", " print('arg_2:', arg_2)\n", " print('arg_3:', arg_3)\n", " \n", "example_function(1, 2)\n", "example_function(1, 2, 3)\n", "example_function(1, 2, 3, 4)\n", "example_function(1, 2, 3, 4, 5)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "\n", "arg_1: 1\n", "arg_2: 2\n", "arg_3: ()\n", "\n", "arg_1: 1\n", "arg_2: 2\n", "arg_3: (3,)\n", "\n", "arg_1: 1\n", "arg_2: 2\n", "arg_3: (3, 4)\n", "\n", "arg_1: 1\n", "arg_2: 2\n", "arg_3: (3, 4, 5)\n" ] } ], "prompt_number": 31 }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can use a for loop to process these other arguments:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def example_function(arg_1, arg_2, *arg_3):\n", " # Let's look at the argument values.\n", " print('\\narg_1:', arg_1)\n", " print('arg_2:', arg_2)\n", " for value in arg_3:\n", " print('arg_3 value:', value)\n", "\n", "example_function(1, 2)\n", "example_function(1, 2, 3)\n", "example_function(1, 2, 3, 4)\n", "example_function(1, 2, 3, 4, 5)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "\n", "arg_1: 1\n", "arg_2: 2\n", "\n", "arg_1: 1\n", "arg_2: 2\n", "arg_3 value: 3\n", "\n", "arg_1: 1\n", "arg_2: 2\n", "arg_3 value: 3\n", "arg_3 value: 4\n", "\n", "arg_1: 1\n", "arg_2: 2\n", "arg_3 value: 3\n", "arg_3 value: 4\n", "arg_3 value: 5\n" ] } ], "prompt_number": 30 }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can now rewrite the adder() function to accept two or more arguments, and print the sum of those numbers:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def adder(num_1, num_2, *nums):\n", " # This function adds the given numbers together,\n", " # and prints the sum.\n", " \n", " # Start by adding the first two numbers, which\n", " # will always be present.\n", " sum = num_1 + num_2\n", " \n", " # Then add any other numbers that were sent.\n", " for num in nums:\n", " sum = sum + num\n", " \n", " # Print the results.\n", " print(\"The sum of your numbers is %d.\" % sum)\n", " \n", "# Let's add some numbers.\n", "adder(1, 2, 3)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "The sum of your numbers is 6.\n" ] } ], "prompt_number": 55 }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this new version, Python does the following:\n", "\n", "- stores the first value in the calling statement in the argument `num_1`;\n", "- stores the second value in the calling statement in the argument `num_2`;\n", "- stores all other values in the calling statement as a tuple in the argument `nums`.\n", "\n", "We can then \"unpack\" these values, using a for loop. We can demonstrate how flexible this function is by calling it a number of times, with a different number of arguments each time." ] }, { "cell_type": "code", "collapsed": false, "input": [ "def adder(num_1, num_2, *nums):\n", " # This function adds the given numbers together,\n", " # and prints the sum.\n", " \n", " # Start by adding the first two numbers, which\n", " # will always be present.\n", " sum = num_1 + num_2\n", " \n", " # Then add any other numbers that were sent.\n", " for num in nums:\n", " sum = sum + num\n", " \n", " # Print the results.\n", " print(\"The sum of your numbers is %d.\" % sum)\n", "\n", " \n", "# Let's add some numbers.\n", "adder(1, 2)\n", "adder(1, 2, 3)\n", "adder(1, 2, 3, 4)\n", "adder(1, 2, 3, 4, 5)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "The sum of your numbers is 3.\n", "The sum of your numbers is 6.\n", "The sum of your numbers is 10.\n", "The sum of your numbers is 15.\n" ] } ], "prompt_number": 28 }, { "cell_type": "markdown", "metadata": {}, "source": [ "[top](#)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Accepting an arbitrary number of keyword arguments\n", "---\n", "Python also provides a syntax for accepting an arbitrary number of keyword arguments. The syntax looks like this:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def example_function(arg_1, arg_2, **kwargs):\n", " # Let's look at x, y, and z\n", " print('\\narg_1:', arg_1)\n", " print('arg_2:', arg_2)\n", " print('arg_3:', kwargs)\n", " \n", "example_function('a', 'b')\n", "example_function('a', 'b', value_3='c')\n", "example_function('a', 'b', value_3='c', value_4='d')\n", "example_function('a', 'b', value_3='c', value_4='d', value_5='e')" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "\n", "arg_1: a\n", "arg_2: b\n", "arg_3: {}\n", "\n", "arg_1: a\n", "arg_2: b\n", "arg_3: {'value_3': 'c'}\n", "\n", "arg_1: a\n", "arg_2: b\n", "arg_3: {'value_4': 'd', 'value_3': 'c'}\n", "\n", "arg_1: a\n", "arg_2: b\n", "arg_3: {'value_5': 'e', 'value_4': 'd', 'value_3': 'c'}\n" ] } ], "prompt_number": 34 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The third argument has two asterisks in front of it, which tells Python to collect all remaining key-value arguments in the calling statement. This argument is commonly named *kwargs*. We see in the output that these key-values are stored in a dictionary. We can loop through this dictionary to work with all of the values that are passed into the function:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def example_function(arg_1, arg_2, **kwargs):\n", " # Let's look at x, y, and z\n", " print('\\narg_1:', arg_1)\n", " print('arg_2:', arg_2)\n", " for key, value in kwargs.items():\n", " print('arg_3 value:', value)\n", " \n", "example_function('a', 'b')\n", "example_function('a', 'b', value_3='c')\n", "example_function('a', 'b', value_3='c', value_4='d')\n", "example_function('a', 'b', value_3='c', value_4='d', value_5='e')" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "\n", "arg_1: a\n", "arg_2: b\n", "\n", "arg_1: a\n", "arg_2: b\n", "arg_3 value: c\n", "\n", "arg_1: a\n", "arg_2: b\n", "arg_3 value: d\n", "arg_3 value: c\n", "\n", "arg_1: a\n", "arg_2: b\n", "arg_3 value: e\n", "arg_3 value: d\n", "arg_3 value: c\n" ] } ], "prompt_number": 36 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Earlier we created a function that let us describe a person, and we had three things we could describe about a person. We could include their age, their favorite language, and the date they passed away. But that was the only information we could include, because it was the only information that the function was prepared to handle:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def describe_person(first_name, last_name, age=None, favorite_language=None, died=None):\n", " # This function takes in a person's first and last name,\n", " # their age, and their favorite language.\n", " # It then prints this information out in a simple format.\n", " \n", " # Required information:\n", " print(\"First name: %s\" % first_name.title())\n", " print(\"Last name: %s\" % last_name.title())\n", " \n", " # Optional information:\n", " if age:\n", " print(\"Age: %d\" % age)\n", " if favorite_language:\n", " print(\"Favorite language: %s\" % favorite_language)\n", " if died:\n", " print(\"Died: %d\" % died)\n", " \n", " # Blank line at end.\n", " print(\"\\n\")\n", "\n", "describe_person('brian', 'kernighan', favorite_language='C')\n", "describe_person('ken', 'thompson', age=70)\n", "describe_person('adele', 'goldberg', age=68, favorite_language='Smalltalk')\n", "describe_person('dennis', 'ritchie', favorite_language='C', died=2011)\n", "describe_person('guido', 'van rossum', favorite_language='Python')" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "First name: Brian\n", "Last name: Kernighan\n", "Favorite language: C\n", "\n", "\n", "First name: Ken\n", "Last name: Thompson\n", "Age: 70\n", "\n", "\n", "First name: Adele\n", "Last name: Goldberg\n", "Age: 68\n", "Favorite language: Smalltalk\n", "\n", "\n", "First name: Dennis\n", "Last name: Ritchie\n", "Favorite language: C\n", "Died: 2011\n", "\n", "\n", "First name: Guido\n", "Last name: Van Rossum\n", "Favorite language: Python\n", "\n", "\n" ] } ], "prompt_number": 1 }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can make this function much more flexible by accepting any number of keyword arguments. Here is what the function looks like, using the syntax for accepting as many keyword arguments as the caller wants to provide:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def describe_person(first_name, last_name, **kwargs):\n", " # This function takes in a person's first and last name,\n", " # and then an arbitrary number of keyword arguments.\n", " \n", " # Required information:\n", " print(\"First name: %s\" % first_name.title())\n", " print(\"Last name: %s\" % last_name.title())\n", " \n", " # Optional information:\n", " for key in kwargs:\n", " print(\"%s: %s\" % (key.title(), kwargs[key]))\n", " \n", " # Blank line at end.\n", " print(\"\\n\")\n", "\n", "describe_person('brian', 'kernighan', favorite_language='C')\n", "describe_person('ken', 'thompson', age=70)\n", "describe_person('adele', 'goldberg', age=68, favorite_language='Smalltalk')\n", "describe_person('dennis', 'ritchie', favorite_language='C', died=2011)\n", "describe_person('guido', 'van rossum', favorite_language='Python')" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "First name: Brian\n", "Last name: Kernighan\n", "Favorite_Language: C\n", "\n", "\n", "First name: Ken\n", "Last name: Thompson\n", "Age: 70\n", "\n", "\n", "First name: Adele\n", "Last name: Goldberg\n", "Age: 68\n", "Favorite_Language: Smalltalk\n", "\n", "\n", "First name: Dennis\n", "Last name: Ritchie\n", "Favorite_Language: C\n", "Died: 2011\n", "\n", "\n", "First name: Guido\n", "Last name: Van Rossum\n", "Favorite_Language: Python\n", "\n", "\n" ] } ], "prompt_number": 4 }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is pretty neat. We get the same output, and we don't have to include a bunch of if tests to see what kind of information was passed into the function. We always require a first name and a last name, but beyond that the caller is free to provide any keyword-value pair to describe a person. Let's show that any kind of information can be provided to this function. We also clean up the output by replacing any underscores in the keys with a space." ] }, { "cell_type": "code", "collapsed": false, "input": [ "def describe_person(first_name, last_name, **kwargs):\n", " # This function takes in a person's first and last name,\n", " # and then an arbitrary number of keyword arguments.\n", " \n", " # Required information:\n", " print(\"First name: %s\" % first_name.title())\n", " print(\"Last name: %s\" % last_name.title())\n", " \n", " # Optional information:\n", " for key in kwargs:\n", " print(\"%s: %s\" % (key.title().replace('_', ' '), kwargs[key]))\n", " \n", " # Blank line at end.\n", " print(\"\\n\")\n", "\n", "describe_person('brian', 'kernighan', favorite_language='C', famous_book='The C Programming Language')\n", "describe_person('ken', 'thompson', age=70, alma_mater='UC Berkeley')\n", "describe_person('adele', 'goldberg', age=68, favorite_language='Smalltalk')\n", "describe_person('dennis', 'ritchie', favorite_language='C', died=2011, famous_book='The C Programming Language')\n", "describe_person('guido', 'van rossum', favorite_language='Python', company='Dropbox')" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "First name: Brian\n", "Last name: Kernighan\n", "Famous Book: The C Programming Language\n", "Favorite Language: C\n", "\n", "\n", "First name: Ken\n", "Last name: Thompson\n", "Alma Mater: UC Berkeley\n", "Age: 70\n", "\n", "\n", "First name: Adele\n", "Last name: Goldberg\n", "Age: 68\n", "Favorite Language: Smalltalk\n", "\n", "\n", "First name: Dennis\n", "Last name: Ritchie\n", "Famous Book: The C Programming Language\n", "Favorite Language: C\n", "Died: 2011\n", "\n", "\n", "First name: Guido\n", "Last name: Van Rossum\n", "Company: Dropbox\n", "Favorite Language: Python\n", "\n", "\n" ] } ], "prompt_number": 6 }, { "cell_type": "markdown", "metadata": {}, "source": [ "There is plenty more to learn about using functions, but with all of this flexibility in terms of how to accept arguments for your functions you should be able to write simple, clean functions that do exactly what you need them to do." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[top](#)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- - -\n", "[Previous: Dictionaries](http://nbviewer.ipython.org/urls/raw.github.com/ehmatthes/intro_programming/master/notebooks/dictionaries.ipynb) | \n", "[Home](http://nbviewer.ipython.org/urls/raw.github.com/ehmatthes/intro_programming/master/notebooks/index.ipynb) |\n", "[Next: Classes](http://nbviewer.ipython.org/urls/raw.github.com/ehmatthes/intro_programming/master/notebooks/classes.ipynb) | " ] } ], "metadata": {} } ] }