{
 "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
}