{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "f246a4c3",
   "metadata": {},
   "source": [
    "# Tuple Unpacking"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c76e12ff",
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys\n",
    "from pathlib import Path\n",
    "\n",
    "# Find project root by looking for _config.yml\n",
    "current = Path.cwd()\n",
    "for parent in [current, *current.parents]:\n",
    "    if (parent / '_config.yml').exists():\n",
    "        project_root = parent\n",
    "        break\n",
    "else:\n",
    "    project_root = Path.cwd().parent.parent\n",
    "\n",
    "# Add project root to path\n",
    "sys.path.insert(0, str(project_root))\n",
    "\n",
    "# Import shared teaching helpers and cell magics\n",
    "from shared import thinkpython, diagram, jupyturtle, structshape\n",
    "from shared.download import download\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8cc0fd06",
   "metadata": {},
   "source": [
    "## Basic Unpacking\n",
    "\n",
    "You can assign multiple variables from a tuple or any sequence."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "e9d67087",
   "metadata": {},
   "outputs": [],
   "source": [
    "a, b = 1, 2"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5c0e86d9",
   "metadata": {},
   "source": [
    "Values are assigned left to right. In this example, `a` gets `1` and `b` gets `2`.\n",
    "We can display the results like this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "bd0acf1f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(1, 2)"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a, b"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3dedfbbf",
   "metadata": {},
   "source": [
    "More generally, if the left side is a tuple of variables, the right side can be any sequence - a string, list, or tuple.\n",
    "For example, to split an email address into a user name and domain:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "d3e0f3bc",
   "metadata": {},
   "outputs": [],
   "source": [
    "email = 'monty@python.org'\n",
    "username, domain = email.split('@')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "13eec68b",
   "metadata": {},
   "source": [
    "`split` returns a list with two elements; the first goes to `username`, the second to `domain`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "e6356b3b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('monty', 'python.org')"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "username, domain"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a12a2321",
   "metadata": {},
   "source": [
    "The number of variables on the left and values on the right must **match**; otherwise you get a `ValueError`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "03f37c17",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%expect ValueError\n",
    "a, b = 1, 2, 3\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3ca9d55d",
   "metadata": {},
   "source": [
    "Tuple assignment is a clean way to swap two variables.\n",
    "With conventional assignments, you need a temporary variable:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "c450eadc",
   "metadata": {},
   "outputs": [],
   "source": [
    "temp = a\n",
    "a = b\n",
    "b = temp"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "60780c74",
   "metadata": {},
   "source": [
    "That works, but tuple assignment does the same swap without a temporary variable."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "88c040f3",
   "metadata": {},
   "outputs": [],
   "source": [
    "a, b = b, a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "162f280a",
   "metadata": {
    "tags": [
     "thebe-interactive"
    ]
   },
   "outputs": [],
   "source": [
    "### Exercise: Tuple Assignment\n",
    "#   1. Unpack the tuple (10, 20, 30) into three variables x, y, z and print them.\n",
    "#   2. Split the email address 'ada@lovelace.org' into username and domain using\n",
    "#      split('@') and tuple assignment, then print both.\n",
    "#   3. Swap the values of two variables a=5, b=99 using tuple assignment.\n",
    "### Your code starts here.\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "### Your code ends here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "ce640574",
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10 20 30\n",
      "ada lovelace.org\n",
      "99 5\n"
     ]
    }
   ],
   "source": [
    "### solution\n",
    "\n",
    "x, y, z = (10, 20, 30)\n",
    "print(x, y, z)                                  # 10 20 30\n",
    "\n",
    "username, domain = 'ada@lovelace.org'.split('@')\n",
    "print(username, domain)                          # ada lovelace.org\n",
    "\n",
    "a, b = 5, 99\n",
    "a, b = b, a\n",
    "print(a, b)                                      # 99 5"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d9dc89e7",
   "metadata": {},
   "source": [
    "## Starred Unpacking\n",
    "\n",
    "Use a starred name to capture \"the rest\" of a sequence during assignment."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "78dca9c7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(0, [1, 2, 3, 4], 5)"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "first, *middle, last = range(6)\n",
    "first, middle, last"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0209e908",
   "metadata": {},
   "source": [
    "This works because all expressions on the right side are evaluated before any assignments.\n",
    "\n",
    "Tuple assignment is also handy in `for` loops.\n",
    "For example, to loop through the items in a dictionary, use the `items` method."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a77dfb70",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'dict_items'>\n",
      "one -> 1\n",
      "two -> 2\n"
     ]
    }
   ],
   "source": [
    "d = {'one': 1, 'two': 2}\n",
    "print(type(d.items()))\n",
    "for item in d.items():          # items() returns a dict_items view of key-value pairs\n",
    "    key, value = item\n",
    "    print(key, '->', value)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "912d40e4",
   "metadata": {},
   "source": [
    "Each time through the loop, `item` is a tuple with a key and its value.\n",
    "We can unpack it directly:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "0e0c0e39",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "one -> 1\n",
      "two -> 2\n"
     ]
    }
   ],
   "source": [
    "for key, value in d.items():\n",
    "    print(key, '->', value)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "id": "2da83c89",
   "metadata": {
    "tags": [
     "thebe-interactive"
    ]
   },
   "outputs": [],
   "source": [
    "### Exercise: Starred Unpacking\n",
    "#   Given data = (1, 2, 3, 4, 5, 6)\n",
    "#   1. Unpack the first element into `head`, the last into `tail`,\n",
    "#      and everything in between into `body` using starred unpacking.\n",
    "#   2. Print all three.\n",
    "#   3. Loop over {'x': 10, 'y': 20, 'z': 30}.items() and unpack\n",
    "#      each key-value pair directly in the for statement, printing each pair.\n",
    "### Your code starts here.\n",
    "\n",
    "data = (1, 2, 3, 4, 5, 6)\n",
    "\n",
    "\n",
    "\n",
    "### Your code ends here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "id": "600f798c",
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 [2, 3, 4, 5] 6\n",
      "x -> 10\n",
      "y -> 20\n",
      "z -> 30\n"
     ]
    }
   ],
   "source": [
    "### solution\n",
    "\n",
    "data = (1, 2, 3, 4, 5, 6)\n",
    "\n",
    "head, *body, tail = data\n",
    "print(head, body, tail)      # 1 [2, 3, 4, 5] 6\n",
    "\n",
    "for key, value in {'x': 10, 'y': 20, 'z': 30}.items():\n",
    "    print(key, '->', value)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6070e933",
   "metadata": {},
   "source": [
    "Now `key` and `value` are assigned on each iteration."
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "Tags",
  "kernelspec": {
   "display_name": ".venv",
   "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.13.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
