{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# `*args` and `**kwargs`\n", "\n", "Work with Python long enough, and eventually you will encounter `*args` and `**kwargs`. These strange terms show up as parameters in function definitions. What do they do? Let's review a simple function:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5.0" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def myfunc(a,b):\n", " return sum((a,b))*.05\n", "\n", "myfunc(40,60)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This function returns 5% of the sum of **a** and **b**. In this example, **a** and **b** are *positional* arguments; that is, 40 is assigned to **a** because it is the first argument, and 60 to **b**. Notice also that to work with multiple positional arguments in the `sum()` function we had to pass them in as a tuple.\n", "\n", "What if we want to work with more than two numbers? One way would be to assign a *lot* of parameters, and give each one a default value." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6.0" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def myfunc(a=0,b=0,c=0,d=0,e=0):\n", " return sum((a,b,c,d,e))*.05\n", "\n", "myfunc(40,60,20)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Obviously this is not a very efficient solution, and that's where `*args` comes in.\n", "\n", "## `*args`\n", "\n", "When a function parameter starts with an asterisk, it allows for an *arbitrary number* of arguments, and the function takes them in as a tuple of values. Rewriting the above function:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6.0" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def myfunc(*args):\n", " return sum(args)*.05\n", "\n", "myfunc(40,60,20)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice how passing the keyword \"args\" into the `sum()` function did the same thing as a tuple of arguments.\n", "\n", "It is worth noting that the word \"args\" is itself arbitrary - any word will do so long as it's preceded by an asterisk. To demonstrate this:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6.0" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def myfunc(*spam):\n", " return sum(spam)*.05\n", "\n", "myfunc(40,60,20)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `**kwargs`\n", "\n", "Similarly, Python offers a way to handle arbitrary numbers of *keyworded* arguments. Instead of creating a tuple of values, `**kwargs` builds a dictionary of key/value pairs. For example:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "My favorite fruit is pineapple\n" ] } ], "source": [ "def myfunc(**kwargs):\n", " if 'fruit' in kwargs:\n", " print(f\"My favorite fruit is {kwargs['fruit']}\") # review String Formatting and f-strings if this syntax is unfamiliar\n", " else:\n", " print(\"I don't like fruit\")\n", " \n", "myfunc(fruit='pineapple')" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "I don't like fruit\n" ] } ], "source": [ "myfunc()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `*args` and `**kwargs` combined\n", "\n", "You can pass `*args` and `**kwargs` into the same function, but `*args` have to appear before `**kwargs`" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "I like eggs and spam and my favorite fruit is cherries\n", "May I have some orange juice?\n" ] } ], "source": [ "def myfunc(*args, **kwargs):\n", " if 'fruit' and 'juice' in kwargs:\n", " print(f\"I like {' and '.join(args)} and my favorite fruit is {kwargs['fruit']}\")\n", " print(f\"May I have some {kwargs['juice']} juice?\")\n", " else:\n", " pass\n", " \n", "myfunc('eggs','spam',fruit='cherries',juice='orange')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Placing keyworded arguments ahead of positional arguments raises an exception:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "ename": "SyntaxError", "evalue": "positional argument follows keyword argument (<ipython-input-8-fc6ff65addcc>, line 1)", "output_type": "error", "traceback": [ "\u001b[1;36m File \u001b[1;32m\"<ipython-input-8-fc6ff65addcc>\"\u001b[1;36m, line \u001b[1;32m1\u001b[0m\n\u001b[1;33m myfunc(fruit='cherries',juice='orange','eggs','spam')\u001b[0m\n\u001b[1;37m ^\u001b[0m\n\u001b[1;31mSyntaxError\u001b[0m\u001b[1;31m:\u001b[0m positional argument follows keyword argument\n" ] } ], "source": [ "myfunc(fruit='cherries',juice='orange','eggs','spam')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As with \"args\", you can use any name you'd like for keyworded arguments - \"kwargs\" is just a popular convention.\n", "\n", "That's it! Now you should understand how `*args` and `**kwargs` provide the flexibilty to work with arbitrary numbers of arguments!" ] } ], "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.6.2" } }, "nbformat": 4, "nbformat_minor": 2 }