{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "4c858ff7",
   "metadata": {},
   "source": [
    "# Unpacking Iterables"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cc098c5d",
   "metadata": {},
   "source": [
    "## A side note about tuples"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "25f62717",
   "metadata": {},
   "source": [
    "What defines a tuple in Python, is not **`()`**, but **`,`**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "191f1bac",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'tuple'>\n",
      "(1, 2, 3)\n"
     ]
    }
   ],
   "source": [
    "x = 1, 2, 3\n",
    "\n",
    "print(type(x))\n",
    "print(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "42ef8f7a",
   "metadata": {},
   "source": [
    "The **`()`** are used to make the tuple clearer.\n",
    "\n",
    "To create a tuple with a single element:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "92da7bcb",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'int'>\n",
      "1\n"
     ]
    }
   ],
   "source": [
    "# will not work as intended\n",
    "x = (1)\n",
    "\n",
    "print(type(x))\n",
    "print(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "27e466f6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'tuple'>\n",
      "(1,)\n"
     ]
    }
   ],
   "source": [
    "x = 1, # or x = (1, ), that is more common\n",
    "\n",
    "print(type(x))\n",
    "print(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1a2c314d",
   "metadata": {},
   "source": [
    "The only exception is when creating an empty tuple:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "a38cd058",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'tuple'>\n",
      "()\n"
     ]
    }
   ],
   "source": [
    "x = () # or x = tuple()\n",
    "\n",
    "print(type(x))\n",
    "print(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d349057c",
   "metadata": {},
   "source": [
    "## Packed Values"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e8af6d87",
   "metadata": {},
   "source": [
    "Packed values refers to values that are **bundled** together in some way.\n",
    "\n",
    "**Tuples** and **Lists** are obvios.\n",
    "\n",
    "```python\n",
    "my_tuple = (1, 2, 3)\n",
    "my_list = (1, 2, 3)\n",
    "```\n",
    "\n",
    "Even a **string** is considered to be a packed value:\n",
    "\n",
    "```python\n",
    "my_str = \"python\"\n",
    "```\n",
    "\n",
    "**Sets** and **dictionaries** are also packed values:\n",
    "\n",
    "```python\n",
    "my_set = {1, 2, 3}\n",
    "my_dict = {\"a\": 1, \"b\": 2, \"c\": 3}\n",
    "```\n",
    "\n",
    "In fact, any **iterable** can be considered a packed value."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9af4130c",
   "metadata": {},
   "source": [
    "## Unpacking Packed Values"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8efd101b",
   "metadata": {},
   "source": [
    "Unpacking is tha act of **splitting** packed values into **individual variables** contained in a list or tuple.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "7c9b2f77",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a=1, b=2, c=3\n"
     ]
    }
   ],
   "source": [
    "# left hand side is a tuple (1, 2, 3)\n",
    "# right hand side is a list\n",
    "a, b, c = [1, 2, 3]\n",
    "\n",
    "print(f\"{a=}, {b=}, {c=}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4a0ce8b5",
   "metadata": {},
   "source": [
    "The unpacking into individual variables is based on the **position** of each element.\n",
    "\n",
    "> Does this remind you of how positional arguments were assigned to parameters in function calls?"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a4a2d786",
   "metadata": {},
   "source": [
    "### Unpacking other Iterables"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "f6c77b25",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a=10, b=20, c='Hello'\n"
     ]
    }
   ],
   "source": [
    "a, b, c = 10, 20, \"Hello\" # right hand side is a tuple\n",
    "\n",
    "print(f\"{a=}, {b=}, {c=}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "d460b701",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a='X', b='Y', c='Z'\n"
     ]
    }
   ],
   "source": [
    "a, b, c = \"XYZ\" # right hand side is a string\n",
    "\n",
    "print(f\"{a=}, {b=}, {c=}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3135cb40",
   "metadata": {},
   "source": [
    "Instead of writing\n",
    "```python\n",
    "a = 10\n",
    "b = 20\n",
    "```\n",
    "we can write\n",
    "```python\n",
    "a, b = 10, 20\n",
    "```\n",
    "\n",
    "In fact, unpacking works with any **iterable** type."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3ff7f3b0",
   "metadata": {},
   "source": [
    "## Simple Application of Unpacking"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "34f02c27",
   "metadata": {},
   "source": [
    "Swapping values of two variables\n",
    "\n",
    "```python\n",
    "a = 10 -> a = 20\n",
    "b = 20 -> b = 10\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "997ce50f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a=10, b=20\n",
      "a=20, b=10\n"
     ]
    }
   ],
   "source": [
    "# traditional approach\n",
    "a = 10\n",
    "b = 20\n",
    "print(f\"{a=}, {b=}\")\n",
    "\n",
    "tmp = a\n",
    "a = b\n",
    "b = tmp\n",
    "print(f\"{a=}, {b=}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "ac44f156",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a=10, b=20\n",
      "a=20, b=10\n"
     ]
    }
   ],
   "source": [
    "# using unpacking \n",
    "a = 10\n",
    "b = 20\n",
    "print(f\"{a=}, {b=}\")\n",
    "\n",
    "a, b = b, a\n",
    "print(f\"{a=}, {b=}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2e0847c8",
   "metadata": {},
   "source": [
    "This works because in Python, the entire RHS is evaluated **first** and **completely**.\n",
    "\n",
    "**then** assinments are made to the LHS."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9ab6f743",
   "metadata": {},
   "source": [
    "### Unpacking Sets and Dictionaries"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "f1ea6324",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "key1\n",
      "key2\n",
      "key3\n"
     ]
    }
   ],
   "source": [
    "d = {\n",
    "    \"key1\": 1,\n",
    "    \"key2\": 2,\n",
    "    \"key3\": 3\n",
    "}\n",
    "\n",
    "for e in d: # e iterates through the keys of dictionary\n",
    "    print(e)\n",
    "    \n",
    "    \n",
    "# for e in d.keys():\n",
    "#     print(e)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "ec201282",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "2\n",
      "3\n"
     ]
    }
   ],
   "source": [
    "for e in d.values(): # e iterates through the values of dictionary\n",
    "    print(e)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "00c42b0d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "('key1', 1)\n",
      "('key2', 2)\n",
      "('key3', 3)\n"
     ]
    }
   ],
   "source": [
    "for e in d.items():\n",
    "    print(e)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f7264216",
   "metadata": {},
   "source": [
    "So, when unpacking **`d`**, we are actually unpacking the **keys** of **`d`**."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "c9d39f09",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a='key1', b='key2', c='key3'\n"
     ]
    }
   ],
   "source": [
    "a, b, c = d\n",
    "\n",
    "print(f\"{a=}, {b=}, {c=}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "64aca275",
   "metadata": {},
   "source": [
    "> Notice\n",
    "```python\n",
    "a, b, c = d\n",
    "```\n",
    "`a = 'key1'`, `b = 'key2'`, `c = 'key3'` or\n",
    "\n",
    "`a = 'key2'`, `b = 'key3'`, `c = 'key1'` or \n",
    "\n",
    "`a = 'key1'`, `b = 'key3'`, `c = 'key2'` or\n",
    "\n",
    "etc...\n",
    "\n",
    "> **Distionaries** and **Sets** are **Unordered** types.\n",
    ">\n",
    "> They can be iterated, but there is **no guarantee** the order of the results will match your literal!\n",
    "\n",
    "In practice, we rarely unpack sets and dictionaries in precisely this way."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f0164eb0",
   "metadata": {},
   "source": [
    "### Example using Sets"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "7bd5b062",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "c\n",
      "e\n",
      "f\n",
      "d\n",
      "b\n",
      "a\n"
     ]
    }
   ],
   "source": [
    "s = {\"a\", \"b\", \"c\", \"d\", \"e\", \"f\"}\n",
    "\n",
    "for e in s: # there is no gaurantee the oerder of the result\n",
    "    print(e)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "e5fa44fa",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a='c', b='e', c='f', d='d', e='b', f='a'\n"
     ]
    }
   ],
   "source": [
    "a, b, c, d, e, f = s\n",
    "\n",
    "print(f\"{a=}, {b=}, {c=}, {d=}, {e=}, {f=}\")"
   ]
  }
 ],
 "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.11.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}