{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "eeb9e8e4",
   "metadata": {},
   "source": [
    "# Functions"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ca5869a5-ec46-43e2-b4e1-ce47f6767328",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    },
    "toc-hr-collapsed": true
   },
   "source": [
    "## Functions Revisited\n",
    "\n",
    "From the \"procedural\" point of view, a function (procedure) is a set of statements that can be called more than once, we use parameters to make our procedures more reusable.\n",
    "\n",
    "This is the \"recipe\" model of programming. \n",
    "A procedure is a recipe, a series of steps to follow to achieve a result.\n",
    "\n",
    "Our first paradigm, **procedural programming** leans heavily on the constructs we've seen: loops, conditionals, and the use of functions to break large procedures into smaller ones.\n",
    "\n",
    "Some languages make a distinction between procedures and functions. In Python we don't make this distinction, but we will soon see another style of programming where we'll think differently about how we use functions.\n",
    "\n",
    "Benefits of procedures (functions):\n",
    "\n",
    "- Encapsulation: package logic so \"user\" does not need to understand *implementation*, only *interface*.\n",
    "- Avoid copy/paste to repeat same task: maximize code reuse and minimize redundancy.\n",
    "- Procedural decomposition: split our program into subtasks (i.e., functions) with separate roles.\n",
    "    - Small functions are easier to test, easier to write, and easier to refactor.\n",
    "    - Makes life easier for debugging, testing, doing maintenance on code."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ec7fa7ef-d733-4bb6-ab52-1dbef3948e86",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "```python\n",
    "def function_name(arg1, arg2, arg3):\n",
    "    \"\"\"\n",
    "         Description of function task \n",
    "\n",
    "         Inputs: \n",
    "             arg1(type): description of arg1 \n",
    "             arg2: description of arg2\n",
    "             arg3: description of arg2\n",
    "\n",
    "         Outputs:\n",
    "             Description of what this function returns \n",
    "    \"\"\"\n",
    "    statement1\n",
    "    statement2\n",
    "    statement3\n",
    "    return value  # optional\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3844e6b9",
   "metadata": {},
   "source": [
    "## Arguments"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2845b7e6-50e9-4047-8cee-03652f6c02b4",
   "metadata": {},
   "source": [
    "In some languages, you can pass arguments by value or by reference.\n",
    "In Python all values are \"passed by assignment\".\n",
    "\n",
    "```python\n",
    "def func(a, b):\n",
    "    ...\n",
    "    \n",
    "x = 7\n",
    "y = [1, 2, 3]\n",
    "func(x, y)\n",
    "\n",
    "# you can think of the function starting with assignments to its parameters\n",
    "a = x\n",
    "b = y\n",
    "```\n",
    "\n",
    "This means mutability determines whether or not a function can modify a parameter in the outer scope."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ca63a3e7",
   "metadata": {},
   "source": [
    "Unless otherwise specified, function arguments are **required**, and can be passed either **by position** or **by name**.\n",
    "\n",
    "As we will see, we can also make optional arguments, as well as arguments that can only be passed by position or by name."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8873aa8d-f4d1-4163-a823-e0b2a5b5a0db",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def calculate_cost(items, tax):\n",
    "    pass\n",
    "\n",
    "calculate_cost([\"salmon\", \"eggs\", \"bagels\"], 0.05)   # items & tax passed by position"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6f3615e8-746b-4776-b3b0-54cbeeedbe48",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b665fccc-1fac-4913-b256-3b0352bd28c3",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "calculate_cost([\"salmon\", \"eggs\", \"bagels\"], tax=0.05) # tax passed by name for clarity"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c05472de",
   "metadata": {},
   "source": [
    "## Optional Arguments\n",
    "\n",
    "Python allows default values to be assigned to function parameters.\n",
    "\n",
    "Arguments with default values are not required.  Passed in values will override default."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "3e20fc7a",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# default arguments\n",
    "def is_it_freezing(temp, is_celsius=True):\n",
    "    if is_celsius:\n",
    "        freezing_line = 0\n",
    "    else:\n",
    "        freezing_line = 32\n",
    "    return temp < freezing_line"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "657bac88",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "False\n",
      "False\n",
      "True\n",
      "True\n"
     ]
    }
   ],
   "source": [
    "print(is_it_freezing(65))\n",
    "print(is_it_freezing(30))\n",
    "print(is_it_freezing(30, False))\n",
    "print(is_it_freezing(-1, is_celsius=True))\n",
    "#print(is_it_freezing())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f5953e4e",
   "metadata": {},
   "source": [
    "You can have as many optional parameters as you wish, but they must all come after any required parameters."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "380a1533",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "non-default argument follows default argument (3809656351.py, line 2)",
     "output_type": "error",
     "traceback": [
      "\u001b[0;36m  Cell \u001b[0;32mIn[5], line 2\u001b[0;36m\u001b[0m\n\u001b[0;31m    def bad_function(a, b=\"spam\", c):\u001b[0m\n\u001b[0m                                  ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m non-default argument follows default argument\n"
     ]
    }
   ],
   "source": [
    "# intentional error\n",
    "def bad_function(a, b=\"spam\", c):\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d61b4aa0",
   "metadata": {},
   "source": [
    "## Argument Matching \n",
    "\n",
    "- Positional arguments are matched from left to right.\n",
    "- Keywords matched by name."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "b25bca3b",
   "metadata": {
    "scrolled": true,
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on built-in function print in module builtins:\n",
      "\n",
      "print(...)\n",
      "    print(value, ..., sep=' ', end='\\n', file=sys.stdout, flush=False)\n",
      "    \n",
      "    Prints the values to a stream, or to sys.stdout by default.\n",
      "    Optional keyword arguments:\n",
      "    file:  a file-like object (stream); defaults to the current sys.stdout.\n",
      "    sep:   string inserted between values, default a space.\n",
      "    end:   string appended after the last value, default a newline.\n",
      "    flush: whether to forcibly flush the stream.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# print() as an example [call help to see docstring]\n",
    "help(print)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "6a31dd7f-f251-4d40-b65d-74e2e35edb9e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hello world ~ ;\n",
      "hello world\n"
     ]
    }
   ],
   "source": [
    "print(\"hello\", \"world\", \"~\", \";\")\n",
    "print(\"hello\", \"world\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "c88ce0c2",
   "metadata": {
    "scrolled": true,
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Something~something else~a third thing!\n",
      "Something*something else*a third thing!\n"
     ]
    }
   ],
   "source": [
    "print(\"Something\", \"something else\", \"a third thing\", sep=\"~\", end=\"!\\n\")\n",
    "print(\"Something\", \"something else\", \"a third thing\", sep=\"*\", end=\"!\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7da7a685",
   "metadata": {},
   "source": [
    "## keyword and positional-only arguments\n",
    "\n",
    "Including a bare `*` as a parameter means everything after can only be passed by keyword.\n",
    "\n",
    "\n",
    "For example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a144f732",
   "metadata": {},
   "outputs": [],
   "source": [
    "def request_page(url, verify, cache=True, retry_if_fail=False, send_cookies=False, https_only=True):\n",
    "    pass\n",
    "\n",
    "request_page(\"https://example.com\", True, False, True, False)\n",
    "# or was it \n",
    "#request_page(\"https://example.com\", True, send_cookies=False, https_only=False, cache=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "c3961865",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# instead\n",
    "def request_page(url, *, verify, follow_redirects=False, cache=True, send_cookies=False, https_only=True):\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "be69d6e3",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "request_page(\"https://example.com\", verify=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6423e576",
   "metadata": {},
   "source": [
    "Including a bare `/` means everything beforehand is positional only:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d7f599fe-cb57-40c8-b337-7d9ccb8f32b9",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "request_page(verify=False, cache=False, url=\"https://example.com\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8720d3db",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def pos_only(x1, x2, /):\n",
    "    print(x1, x2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fe16b03f",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "pos_only(\"hello\", \"world\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "ed248450",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "min(1, 2)  # arguments to min are positional only, we don't need to know if they are a, b or x, y, or first, second"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "83bd29a5-b208-4722-aed5-d3c8e56f11af",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "#help(min)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c57d14e7",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "pos_only(\"hello\", \"world\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8c9ceab8",
   "metadata": {},
   "source": [
    "## Unpacking/Splatting\n",
    "\n",
    "`*` and `**` are also known as unpacking or splatting operators.\n",
    "\n",
    "When in a function signature, they coalesce arguments into a `tuple` and `dict` as we've seen.\n",
    "\n",
    "When used on a parameter when calling a function, they \"unpack\" the values from a sequence or dict."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "8c76791c",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def takes_many(a, b, c, d):\n",
    "    print(f\"{a=} {b=} {c=} {d=}\")\n",
    "\n",
    "three = [\"A\", \"B\", \"C\"]\n",
    "four = (1, 2, 3, 4)\n",
    "five = (False, False, False, False, False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "6b026e40",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a=1 b=2 c=3 d=4\n"
     ]
    }
   ],
   "source": [
    "takes_many(*four)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "81227c1c-e7ef-43b9-834a-426df03e4901",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a=4 b='A' c='B' d='C'\n"
     ]
    }
   ],
   "source": [
    "takes_many(4, *three)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "25afe0fa",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "takes_many() takes 4 positional arguments but 5 were given",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[20], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mtakes_many\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mfive\u001b[49m\u001b[43m)\u001b[49m\n",
      "\u001b[0;31mTypeError\u001b[0m: takes_many() takes 4 positional arguments but 5 were given"
     ]
    }
   ],
   "source": [
    "takes_many(*five)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "f83e1d2c",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a='sun' b='mars' c='venus' d='moon'\n"
     ]
    }
   ],
   "source": [
    "# double-splat\n",
    "keywords = {\"a\": \"sun\", \"c\": \"venus\", \"b\": \"mars\"}\n",
    "takes_many(**keywords, d=\"moon\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "68665bc7-0325-4843-b73b-e93d2f0ef9a5",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "a207252c",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import math\n",
    "\n",
    "\n",
    "def distance(x1, y1, x2, y2):\n",
    "    \"\"\"\n",
    "    Find distance between two points.\n",
    "    \n",
    "    Inputs:\n",
    "        point1: 2-element tuple (x, y)\n",
    "        point2: 2-element tuple (x, y)\n",
    "\n",
    "    Output: Distance between point1 and point2 (float).\n",
    "    \"\"\"\n",
    "    return math.sqrt(math.pow(x2-x1, 2) + math.pow(y2-y1, 2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "269d6da6",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2.23606797749979"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# we can use sequence-unpacking to turn tuples/lists into multiple arguments\n",
    "a = (3, 4)\n",
    "b = [5, 5]\n",
    "distance(*a, *b) # our 2 parameters become 4"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "ccbc56bf-fd3d-4c55-bad3-6327ddc4e1da",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "([1, 2], [3, 4])"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = [1,2,3,4]\n",
    "a[0:2], a[2:]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cb8464bd",
   "metadata": {},
   "outputs": [],
   "source": [
    "http_params = {\"verify\": False, \"https_only\": True, \"timeout_sec\": 3}\n",
    "request_page(\"http://example.com\", **http_params)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "12ea48b3-2e4f-488c-bc14-fc3062785515",
   "metadata": {},
   "outputs": [],
   "source": [
    "def fn1(url, kw1=None, kw2=None, kw3=None):\n",
    "    ...\n",
    "    \n",
    "def fn2(url, **kwargs):\n",
    "    if is_valid(url):\n",
    "        kwargs[\"additional_arg\"] = 4\n",
    "        return fn1(url, **kwargs)\n",
    "    return None\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a864fe5d",
   "metadata": {},
   "source": [
    "## Caveat: Mutable Default Arguments\n",
    "\n",
    "The `def` line of a function is only evaluated once, not every time the function is called.\n",
    "\n",
    "This can feel surprising at first, but important to understand & remember that only the inner-block of a function is executed on each call.\n",
    "\n",
    "This is a common cause of bugs if a mutable is a part of the default arguments."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "id": "7b668730",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def add_many(item, n, base_list=[]):\n",
    "    print(id(base_list))\n",
    "    base_list.extend([item] * n)\n",
    "    return base_list"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "id": "33f08dc3",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4420093440\n",
      "['cow', 'bear', 'bear', 'bear', 'fish', 'fish', 'fish', 'fish', 'fish']\n"
     ]
    }
   ],
   "source": [
    "# passing in a list for base_list works as expected...\n",
    "animals = [\"cow\"]\n",
    "print(id(animals))\n",
    "add_many(\"bear\", 3, animals)\n",
    "add_many(\"fish\", 5, animals)\n",
    "print(animals)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "id": "6f032467",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['dog', 'dog', 'dog']\n"
     ]
    }
   ],
   "source": [
    "# let's invoke without a base_list parameter\n",
    "animals2 = add_many(\"dog\", 3)\n",
    "print(animals2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "id": "e59a4e7e",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['turtle', 'turtle', 'turtle', 'turtle']\n"
     ]
    }
   ],
   "source": [
    "animals3 = add_many(\"turtle\", 4)\n",
    "print(animals3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8bbae145",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "animals2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "21265990-0d35-4da9-a95d-60020792eb90",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "id": "fb1c69f0",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 51,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "animals2 is animals3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a28b0699-1b47-417d-bea6-7be082bd228f",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "id": "8d35c154",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# fixed version\n",
    "def add_many(item, n, base_list=None):\n",
    "    if base_list is None:\n",
    "        base_list = []\n",
    "    base_list.extend([item] * n)\n",
    "    return base_list"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "id": "1e61b0a9-df65-4d4a-a786-77e50aa5d977",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4419504192\n",
      "['fish', 'fish', 'fish']\n",
      "4413843968\n"
     ]
    }
   ],
   "source": [
    "temp = []\n",
    "print(id(temp))\n",
    "returned = add_many(\"fish\", 3)\n",
    "print(returned)\n",
    "print(id(returned))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "id": "ef40c301",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "\n",
    "# this usage takes advantage of the fact that the cache_dict is shared between calls\n",
    "# this is fine to do if you're sure you know what you're doing.\n",
    "# Since it is unexpected/tricky, should definitely be commented\n",
    "def add_cached(x, y, cache_dict={}):\n",
    "    print(cache_dict)\n",
    "    if (x, y) not in cache_dict:\n",
    "        print(\"did calculation\", x, y)\n",
    "        cache_dict[x, y] = x + y\n",
    "    return cache_dict[x, y]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "id": "8ea61c05",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{}\n",
      "did calculation 4 5\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "9"
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add_cached(4, 5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "id": "2f5daa3e-2147-436e-aacc-e75349735b83",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{(4, 5): 9}\n",
      "did calculation 6 10\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "16"
      ]
     },
     "execution_count": 54,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add_cached(6, 10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "id": "81baf040-cbae-4856-af98-47036c3db7c0",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{(4, 5): 9, (6, 10): 16}\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "9"
      ]
     },
     "execution_count": 55,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add_cached(4, 5)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "097a61f9",
   "metadata": {},
   "source": [
    "## Variable Length Arguments\n",
    "\n",
    "Sometimes we want a function that can take any number of parameters (seen above in `print`).\n",
    "\n",
    "Collect arbitrary positional arguments with `*param_name`. (Often `*args`)\n",
    "\n",
    "Collect arbitrary named arguments with `**param_name`. (Often `**kwargs`)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "id": "51243d5a",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# *args example\n",
    "\n",
    "def add_many(*args):\n",
    "    #print(args, type(args))\n",
    "    total = 0\n",
    "    for num in args:\n",
    "        total += num\n",
    "    return total"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "id": "bd472168-a05a-4727-83d1-74dad537396f",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "15"
      ]
     },
     "execution_count": 61,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add_many(1, 2, 3, 4, 5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f140e30e",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "id": "f28c1819",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# **kwargs example\n",
    "\n",
    "def show_table(**kwargs):\n",
    "    for name, val in kwargs.items():\n",
    "        print(f\"{name:>10} | {val}\")\n",
    "        \n",
    "# Using advanced string formatting, see https://docs.python.org/3/library/string.html#formatstrings"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ec1b868e-a647-4e14-b259-2dcc3243caf8",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "id": "8a88495d",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "      spam | 100\n",
      "      eggs | 12\n",
      "     other | 42.0\n"
     ]
    }
   ],
   "source": [
    "show_table(spam=100, eggs=12, other=42.0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "id": "fbde7e32-9d6c-42ff-b4d7-47d2911b907a",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "(2, 3, 4)\n",
      "5\n",
      "{'c': 1, 'b': 2}\n"
     ]
    }
   ],
   "source": [
    "def fn(a, *args, n=5, **kwargs):\n",
    "    print(a, args, n, kwargs, sep=\"\\n\")\n",
    "\n",
    "fn(1, 2, 3, 4, c=1, b=2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "66278fda-4e78-4e8e-bf65-7fc3e25b7acd",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "#def example(*args, *args2) # not allowed"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "id": "0fbba710-5d89-4cf8-9472-807e759b5b77",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "keyword argument repeated: x (231222369.py, line 1)",
     "output_type": "error",
     "traceback": [
      "\u001b[0;36m  Cell \u001b[0;32mIn[68], line 1\u001b[0;36m\u001b[0m\n\u001b[0;31m    fn(x=7, x=8)\u001b[0m\n\u001b[0m            ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m keyword argument repeated: x\n"
     ]
    }
   ],
   "source": [
    "fn(x=7, x=8)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "id": "f25c3255-4443-4505-9f63-bfda97548fb6",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "(2, 3)\n",
      "5\n",
      "{'rest': 5}\n"
     ]
    }
   ],
   "source": [
    "fn(1, 2, 3, rest=5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "id": "0f130dfd-ce4a-4baf-9603-69556ca10f34",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4"
      ]
     },
     "execution_count": 71,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "max(1, 2, 3, 4)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "87a59123",
   "metadata": {},
   "source": [
    "## Discussion\n",
    "\n",
    "\n",
    "- What types are `args` and `kwargs`?\n",
    "- When would you use `*args`? `**kwargs`?\n",
    "- What would `func(*args1, *args2)` do?\n",
    "- f,g,h,k examples\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "db1ab116-c4a0-4971-83c3-d5d2c7730221",
   "metadata": {},
   "source": [
    "### When should you use defaults, name-only, positional-only?\n",
    "\n",
    "Your function provides an \"interface\" for other programmers to interact with.\n",
    "\n",
    "Proper choices help other programmers understand how to call your functions and should be chosen to make things easier for others.\n",
    "\n",
    "What would you prefer?\n",
    "\n",
    "`get(\"https://example.com\", [500, 501, 503], 2.5, 2, False)`\n",
    "\n",
    "or\n",
    "\n",
    "`get(\"https://example.com\", retry_if=[500, 501, 503], timeout_sec=2.5, retries=2, verify_ssl=False)`\n",
    "\n",
    "**Always consider \"future you\" among those hypothetical \"other programmers\".**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "id": "ce594c78-0561-488a-9353-17c4327925a8",
   "metadata": {},
   "outputs": [],
   "source": [
    "def process_data(url, **kwargs):\n",
    "    kwargs[\"verify\"] = True\n",
    "    data = fetch_url(url, **kwargs)\n",
    "    ...\n",
    "    return ..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8c9bf05b-d27e-42b2-ae14-dec11b6a7285",
   "metadata": {},
   "outputs": [],
   "source": [
    "process_data(\"https://url.com\", verify=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "id": "59eb41da",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# two required args\n",
    "def f(x, y):\n",
    "    print(f\"{x=} {y=}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "id": "04b08446",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x=1 y=2\n"
     ]
    }
   ],
   "source": [
    "f(1, 2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "id": "b7573001",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "f() missing 1 required positional argument: 'y'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[75], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mf\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m)\u001b[49m\n",
      "\u001b[0;31mTypeError\u001b[0m: f() missing 1 required positional argument: 'y'"
     ]
    }
   ],
   "source": [
    "f(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "id": "da2271fa",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# a default argument\n",
    "def g(x, y=3):\n",
    "    print(f\"{x=} {y=}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "id": "01b06cd7-f7a7-41e2-8527-bf75828fe659",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "g() missing 1 required positional argument: 'x'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[77], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mg\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n",
      "\u001b[0;31mTypeError\u001b[0m: g() missing 1 required positional argument: 'x'"
     ]
    }
   ],
   "source": [
    "g()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 78,
   "id": "c6d0abdf",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x=1 y=3\n"
     ]
    }
   ],
   "source": [
    "g(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 79,
   "id": "e5eee452",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x=1 y=2\n"
     ]
    }
   ],
   "source": [
    "g(1, 2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 80,
   "id": "a08c34e4",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# all default args\n",
    "def h(x=\"abc\", y=3, z=True):\n",
    "    print(f\"{x=} {y=} {z=}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "id": "f62cdffa",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x='abc' y=3 z=True\n"
     ]
    }
   ],
   "source": [
    "h()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 82,
   "id": "ee097684",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x=1 y=3 z=True\n"
     ]
    }
   ],
   "source": [
    "h(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "id": "29fdaad7",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x='abc' y=3 z=False\n"
     ]
    }
   ],
   "source": [
    "h(z=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 84,
   "id": "98687995",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "h() got multiple values for argument 'x'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[84], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mh\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m3\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mx\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m)\u001b[49m\n",
      "\u001b[0;31mTypeError\u001b[0m: h() got multiple values for argument 'x'"
     ]
    }
   ],
   "source": [
    "h(3, x=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 85,
   "id": "56b563f5",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x=3 y='xyz' z=True\n"
     ]
    }
   ],
   "source": [
    "h(3, y=\"xyz\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 86,
   "id": "302c95cd",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# args, kwargs, and positional args\n",
    "def j(x, *args, y=3, **kwargs):\n",
    "    print(f\"{x=} {y=} {args=} {kwargs=}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 87,
   "id": "2cb35398",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x=1 y=3 args=(2, 3, 4, 5, 6, 7, 8, 9) kwargs={}\n"
     ]
    }
   ],
   "source": [
    "j(1, 2, 3, 4, 5, 6, 7, 8, 9, y=3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "id": "24c27e31",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "j() missing 1 required positional argument: 'x'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[88], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mj\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfoo\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m3\u001b[39;49m\u001b[43m)\u001b[49m\n",
      "\u001b[0;31mTypeError\u001b[0m: j() missing 1 required positional argument: 'x'"
     ]
    }
   ],
   "source": [
    "j(foo=3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 89,
   "id": "88637937",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x=1 y=3 args=() kwargs={'foo': 3}\n"
     ]
    }
   ],
   "source": [
    "j(1, foo=3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 91,
   "id": "c2e03046",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x=1 y=3 args=() kwargs={'foo': 3}\n"
     ]
    }
   ],
   "source": [
    "j(foo=3, x=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 92,
   "id": "368cd5e7",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "j() got multiple values for argument 'x'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[92], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mj\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m2\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m3\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m4\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mx\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m5\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mz\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m10\u001b[39;49m\u001b[43m)\u001b[49m\n",
      "\u001b[0;31mTypeError\u001b[0m: j() got multiple values for argument 'x'"
     ]
    }
   ],
   "source": [
    "j(1, 2, 3, 4, x=5, z=10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "191d145d-0117-4755-8f36-17f18d77b98b",
   "metadata": {},
   "outputs": [],
   "source": [
    "j(1, foo=3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7247e66c-009d-47d8-bf6e-4363ab42bb24",
   "metadata": {},
   "outputs": [],
   "source": [
    "j(foo=3, x=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bc4897ad-321d-4c7e-b8f7-89a2d3020db7",
   "metadata": {},
   "outputs": [],
   "source": [
    "def my_func(url, **kwargs):\n",
    "    kwargs[\"verify_ssl\"] = True\n",
    "    request_page(url, **kwargs)\n",
    "    ...\n",
    "    return ..."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.10.15"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": false,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {
    "height": "calc(100% - 180px)",
    "left": "10px",
    "top": "150px",
    "width": "192px"
   },
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
