{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "9c2cd54a",
   "metadata": {},
   "source": [
    "# 01 · Python Data Structures\n",
    "\n",
    "The building blocks every Python program is made of: the four core collections, the specialised\n",
    "containers in the standard library, functions, a first look at classes, comprehensions and error\n",
    "handling. Every example is short and runnable; change it and run it again.\n",
    "\n",
    "**You will learn**\n",
    "1. Lists, tuples, dictionaries and sets, and when to use each one\n",
    "2. The `collections` module: `deque`, `Counter`, `defaultdict`, `namedtuple`\n",
    "3. Functions: default arguments, `*args`, `**kwargs`, lambdas\n",
    "4. Classes in five minutes (module 05 goes much deeper)\n",
    "5. Comprehensions and generators\n",
    "6. Exceptions\n",
    "7. Why the choice of structure matters: measuring lookup speed"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1b220887",
   "metadata": {},
   "source": [
    "## 1. Lists\n",
    "Ordered, **mutable** and allow duplicates. The default container for \"a sequence of things\"."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "7464386e",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-17T23:21:20.660553Z",
     "iopub.status.busy": "2026-10-17T23:21:20.660063Z",
     "iopub.status.idle": "2026-10-17T23:21:20.674212Z",
     "shell.execute_reply": "2026-10-17T23:21:20.672260Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['apple', 'apricot', 'cherry'] | popped: date\n",
      "[2, 3, 4] [0, 2, 4, 6, 8] [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]\n",
      "['Fig', 'apple', 'pear'] ['apple', 'Fig', 'pear'] ['apple', 'pear', 'Fig']\n"
     ]
    }
   ],
   "source": [
    "fruits = [\"apple\", \"banana\", \"cherry\"]\n",
    "fruits.append(\"date\")             # add at the end\n",
    "fruits.insert(1, \"apricot\")       # add at a position\n",
    "fruits.remove(\"banana\")           # remove the first matching value\n",
    "last = fruits.pop()               # remove and return the last item\n",
    "print(fruits, \"| popped:\", last)\n",
    "\n",
    "# slicing: [start:stop:step], stop is excluded\n",
    "nums = list(range(10))\n",
    "print(nums[2:5], nums[::2], nums[::-1])\n",
    "\n",
    "# sorting: sorted() returns a new list, .sort() sorts in place\n",
    "words = [\"pear\", \"Fig\", \"apple\"]\n",
    "print(sorted(words), sorted(words, key=str.lower), sorted(words, key=len, reverse=True))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "549183b4",
   "metadata": {},
   "source": [
    "## 2. Tuples\n",
    "Ordered and **immutable**. Use them for fixed groups of values (a point, a database row) and to return\n",
    "several values from a function. Being immutable, they can be dictionary keys or set members."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "d69a0d0f",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-17T23:21:20.678715Z",
     "iopub.status.busy": "2026-10-17T23:21:20.678216Z",
     "iopub.status.idle": "2026-10-17T23:21:20.689070Z",
     "shell.execute_reply": "2026-10-17T23:21:20.686702Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(3, 4) ('only_one',) 3 4\n",
      "2 9\n",
      "1 [2, 3, 4] 5\n"
     ]
    }
   ],
   "source": [
    "point = (3, 4)\n",
    "single = (\"only_one\",)            # the trailing comma makes it a tuple\n",
    "x, y = point                      # unpacking\n",
    "print(point, single, x, y)\n",
    "\n",
    "def min_max(values):\n",
    "    return min(values), max(values)   # returns a tuple\n",
    "\n",
    "lo, hi = min_max([7, 2, 9, 4])\n",
    "print(lo, hi)\n",
    "\n",
    "first, *middle, last = [1, 2, 3, 4, 5]   # extended unpacking\n",
    "print(first, middle, last)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d2aa8a01",
   "metadata": {},
   "source": [
    "## 3. Dictionaries\n",
    "Key-value pairs, mutable, and insertion-ordered since Python 3.7. Lookup by key is fast, independent of\n",
    "the dictionary's size."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "2b850176",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-17T23:21:20.694352Z",
     "iopub.status.busy": "2026-10-17T23:21:20.693820Z",
     "iopub.status.idle": "2026-10-17T23:21:20.705756Z",
     "shell.execute_reply": "2026-10-17T23:21:20.703556Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "n/a\n",
      "  name: Alice\n",
      "   age: 31\n",
      "  city: Berlin\n",
      " email: alice@example.com\n",
      "{'city': 'Berlin', 'active': True, 'name': 'Alice', 'age': 31, 'email': 'alice@example.com'} {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}\n"
     ]
    }
   ],
   "source": [
    "person = {\"name\": \"Alice\", \"age\": 30, \"city\": \"Berlin\"}\n",
    "person[\"age\"] = 31                       # update\n",
    "person[\"email\"] = \"alice@example.com\"    # add\n",
    "print(person.get(\"phone\", \"n/a\"))        # safe access with a default\n",
    "for key, value in person.items():\n",
    "    print(f\"{key:>6}: {value}\")\n",
    "\n",
    "# merging and dict comprehensions\n",
    "defaults = {\"city\": \"Unknown\", \"active\": True}\n",
    "merged = defaults | person               # right-hand side wins (Python 3.9+)\n",
    "squares = {n: n * n for n in range(5)}\n",
    "print(merged, squares)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "13e90269",
   "metadata": {},
   "source": [
    "## 4. Sets\n",
    "Unordered, mutable, **unique** elements. Ideal for membership tests and removing duplicates."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "0e7ecada",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-17T23:21:20.710565Z",
     "iopub.status.busy": "2026-10-17T23:21:20.710093Z",
     "iopub.status.idle": "2026-10-17T23:21:20.718425Z",
     "shell.execute_reply": "2026-10-17T23:21:20.716328Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{1, 2, 3, 4, 5} {3} {1, 2} {1, 2, 4, 5}\n",
      "[1, 2, 3]\n",
      "True frozenset({1, 2, 3})\n"
     ]
    }
   ],
   "source": [
    "a, b = {1, 2, 3}, {3, 4, 5}\n",
    "print(a | b, a & b, a - b, a ^ b)        # union, intersection, difference, symmetric difference\n",
    "\n",
    "nums = [1, 2, 2, 3, 3, 3]\n",
    "print(sorted(set(nums)))                 # dedup (sets do not keep order, so sort if you need one)\n",
    "print(3 in a, frozenset(a))              # frozenset: immutable set, usable as a dict key"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "16c9f98c",
   "metadata": {},
   "source": [
    "## 5. Specialised containers: `collections`\n",
    "The standard library has ready-made structures for common patterns. Reaching for them makes code shorter\n",
    "and often faster.\n",
    "\n",
    "| Structure | Use it for |\n",
    "|---|---|\n",
    "| `deque` | queues and sliding windows: fast append/pop at **both** ends |\n",
    "| `Counter` | counting things |\n",
    "| `defaultdict` | grouping without \"if key not in dict\" checks |\n",
    "| `namedtuple` | lightweight records with named fields |\n",
    "| `heapq` (module) | always getting the smallest item: priority queues, top-k |"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "29fcc1a3",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-17T23:21:20.722774Z",
     "iopub.status.busy": "2026-10-17T23:21:20.722288Z",
     "iopub.status.idle": "2026-10-17T23:21:20.736463Z",
     "shell.execute_reply": "2026-10-17T23:21:20.734451Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "last 3 readings: [11, 15, 14] mean: 13.333333333333334\n",
      "[('the', 3), ('quick', 1)]\n",
      "{'a': ['apple', 'avocado'], 'b': ['banana', 'blueberry'], 'c': ['cherry']}\n",
      "[City(name='Tokyo', population_m=37.4), City(name='Delhi', population_m=32.9)]\n"
     ]
    }
   ],
   "source": [
    "from collections import deque, Counter, defaultdict, namedtuple\n",
    "import heapq\n",
    "\n",
    "window = deque(maxlen=3)                 # keeps only the last 3 items\n",
    "for reading in [10, 12, 11, 15, 14]:\n",
    "    window.append(reading)\n",
    "print(\"last 3 readings:\", list(window), \"mean:\", sum(window) / len(window))\n",
    "\n",
    "text = \"the quick brown fox jumps over the lazy dog the end\"\n",
    "print(Counter(text.split()).most_common(2))\n",
    "\n",
    "by_letter = defaultdict(list)\n",
    "for word in [\"apple\", \"avocado\", \"banana\", \"blueberry\", \"cherry\"]:\n",
    "    by_letter[word[0]].append(word)\n",
    "print(dict(by_letter))\n",
    "\n",
    "City = namedtuple(\"City\", [\"name\", \"population_m\"])\n",
    "cities = [City(\"Tokyo\", 37.4), City(\"Delhi\", 32.9), City(\"Shanghai\", 29.2), City(\"Madrid\", 6.8)]\n",
    "print(heapq.nlargest(2, cities, key=lambda c: c.population_m))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0c027f6d",
   "metadata": {},
   "source": [
    "## 6. Functions\n",
    "Reusable blocks of code. Type hints document what goes in and out; Python does not enforce them at run\n",
    "time, but editors and checkers such as `mypy` use them."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "9cc80b39",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-17T23:21:20.741502Z",
     "iopub.status.busy": "2026-10-17T23:21:20.741011Z",
     "iopub.status.idle": "2026-10-17T23:21:20.754180Z",
     "shell.execute_reply": "2026-10-17T23:21:20.751915Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello, Alice! Hi, Bob!\n",
      "6 | colour=red, size=M\n",
      "[1, 4, 9] ['ccc', 'bb', 'a']\n"
     ]
    }
   ],
   "source": [
    "def greet(name: str, greeting: str = \"Hello\") -> str:\n",
    "    return f\"{greeting}, {name}!\"\n",
    "\n",
    "print(greet(\"Alice\"), greet(\"Bob\", greeting=\"Hi\"))\n",
    "\n",
    "def total(*args: float) -> float:          # any number of positional arguments\n",
    "    return sum(args)\n",
    "\n",
    "def describe(**kwargs) -> str:             # any number of keyword arguments\n",
    "    return \", \".join(f\"{k}={v}\" for k, v in kwargs.items())\n",
    "\n",
    "print(total(1, 2, 3), \"|\", describe(colour=\"red\", size=\"M\"))\n",
    "\n",
    "square = lambda n: n ** 2                   # small anonymous function\n",
    "print(list(map(square, [1, 2, 3])), sorted([\"bb\", \"a\", \"ccc\"], key=lambda s: -len(s)))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6d3d0290",
   "metadata": {},
   "source": [
    "**A classic trap: mutable default arguments.** The default value is created once, when the function is\n",
    "defined, not on every call."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "4a598b83",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-17T23:21:20.758599Z",
     "iopub.status.busy": "2026-10-17T23:21:20.758114Z",
     "iopub.status.idle": "2026-10-17T23:21:20.767174Z",
     "shell.execute_reply": "2026-10-17T23:21:20.765686Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['a', 'b'] ['a', 'b']\n",
      "['a'] ['b']\n"
     ]
    }
   ],
   "source": [
    "def add_item_bad(item, items=[]):\n",
    "    items.append(item)\n",
    "    return items\n",
    "\n",
    "def add_item_good(item, items=None):\n",
    "    items = [] if items is None else items\n",
    "    items.append(item)\n",
    "    return items\n",
    "\n",
    "print(add_item_bad(\"a\"), add_item_bad(\"b\"))     # the same list is reused: ['a', 'b'] twice\n",
    "print(add_item_good(\"a\"), add_item_good(\"b\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a3553cce",
   "metadata": {},
   "source": [
    "## 7. Classes in five minutes\n",
    "A class is a blueprint, an object is one instance of it, and methods are functions that belong to the\n",
    "class. Module 05 covers object-oriented programming in depth; this is just enough to read code that uses it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "27bb381f",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-17T23:21:20.772711Z",
     "iopub.status.busy": "2026-10-17T23:21:20.772235Z",
     "iopub.status.idle": "2026-10-17T23:21:20.782681Z",
     "shell.execute_reply": "2026-10-17T23:21:20.780610Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dog(name='Rex', age=4) Rex says woof Canis familiaris\n"
     ]
    }
   ],
   "source": [
    "class Dog:\n",
    "    species = \"Canis familiaris\"            # class attribute: shared by every dog\n",
    "\n",
    "    def __init__(self, name: str, age: int):\n",
    "        self.name = name                    # instance attributes: one per dog\n",
    "        self.age = age\n",
    "\n",
    "    def speak(self) -> str:\n",
    "        return f\"{self.name} says woof\"\n",
    "\n",
    "    def __repr__(self) -> str:              # how the object prints\n",
    "        return f\"Dog(name={self.name!r}, age={self.age})\"\n",
    "\n",
    "rex = Dog(\"Rex\", 4)\n",
    "print(rex, rex.speak(), Dog.species)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "93a732ed",
   "metadata": {},
   "source": [
    "## 8. Comprehensions and generators\n",
    "Comprehensions build a collection in one readable line. A **generator** produces values lazily, one at a\n",
    "time, so it uses almost no memory even for millions of items. That idea returns in module 03."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "22c19f65",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-17T23:21:20.787501Z",
     "iopub.status.busy": "2026-10-17T23:21:20.786943Z",
     "iopub.status.idle": "2026-10-17T23:21:20.942696Z",
     "shell.execute_reply": "2026-10-17T23:21:20.940780Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0, 4, 16, 36, 64] [(0, 'a'), (0, 'b'), (1, 'a'), (1, 'b'), (2, 'a'), (2, 'b')]\n",
      "list: 8.4 MB, generator: 192 bytes\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sum from the generator:"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " 499999500000\n",
      "[5, 4, 3, 2, 1]\n"
     ]
    }
   ],
   "source": [
    "import sys\n",
    "\n",
    "squares = [n ** 2 for n in range(10) if n % 2 == 0]\n",
    "pairs = [(x, y) for x in range(3) for y in \"ab\"]\n",
    "print(squares, pairs)\n",
    "\n",
    "big_list = [n for n in range(1_000_000)]\n",
    "big_gen = (n for n in range(1_000_000))\n",
    "print(f\"list: {sys.getsizeof(big_list) / 1e6:.1f} MB, generator: {sys.getsizeof(big_gen)} bytes\")\n",
    "print(\"sum from the generator:\", sum(big_gen))\n",
    "\n",
    "def countdown(n):\n",
    "    while n > 0:\n",
    "        yield n          # 'yield' makes this function a generator\n",
    "        n -= 1\n",
    "\n",
    "print(list(countdown(5)))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "322db508",
   "metadata": {},
   "source": [
    "## 9. Exceptions\n",
    "Handle the errors you expect, let the unexpected ones surface. `else` runs only when nothing failed,\n",
    "`finally` always runs (cleanup)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "b1ba6a82",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-17T23:21:20.946494Z",
     "iopub.status.busy": "2026-10-17T23:21:20.946148Z",
     "iopub.status.idle": "2026-10-17T23:21:20.955328Z",
     "shell.execute_reply": "2026-10-17T23:21:20.953469Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "divide(10, 2) finished\n",
      "cannot divide by zero\n",
      "divide(1, 0) finished\n",
      "bad input: unsupported operand type(s) for /: 'str' and 'int'\n",
      "divide('a', 1) finished\n",
      "5.0 None None\n",
      "caught: balance 10, requested 50\n"
     ]
    }
   ],
   "source": [
    "def divide(a, b):\n",
    "    try:\n",
    "        result = a / b\n",
    "    except ZeroDivisionError:\n",
    "        print(\"cannot divide by zero\")\n",
    "        return None\n",
    "    except TypeError as e:\n",
    "        print(\"bad input:\", e)\n",
    "        return None\n",
    "    else:\n",
    "        return result\n",
    "    finally:\n",
    "        print(f\"divide({a!r}, {b!r}) finished\")\n",
    "\n",
    "print(divide(10, 2), divide(1, 0), divide(\"a\", 1))\n",
    "\n",
    "class InsufficientFunds(Exception):\n",
    "    '''A custom exception makes the failure explicit for the caller.'''\n",
    "\n",
    "try:\n",
    "    raise InsufficientFunds(\"balance 10, requested 50\")\n",
    "except InsufficientFunds as e:\n",
    "    print(\"caught:\", e)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e3881182",
   "metadata": {},
   "source": [
    "## 10. Why the choice of structure matters\n",
    "Checking whether an item is **in** a list scans it item by item (time grows with the size, O(n)). A set\n",
    "or dict uses hashing and answers in about the same time whatever its size (O(1) on average). Measured:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "342c6c5e",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-17T23:21:20.959325Z",
     "iopub.status.busy": "2026-10-17T23:21:20.958996Z",
     "iopub.status.idle": "2026-10-17T23:21:23.980284Z",
     "shell.execute_reply": "2026-10-17T23:21:23.978262Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "n=    1,000  list:       13.1 µs   set:   0.16 µs\n",
      "n=   10,000  list:      135.7 µs   set:   0.15 µs\n",
      "n=  100,000  list:     1413.8 µs   set:   0.16 µs\n",
      "n=1,000,000  list:    14831.2 µs   set:   0.17 µs\n"
     ]
    },
    {
     "data": {
      "image/png": "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bNq0Cb169ULHjh2bdHkTajkqKipw8OBBHDt2rMY+2dnZOHjwIP7+++9q2zZt2oQVK1Y0etIEACaxyG9rcuPGDfHss88KT09PYWNjI1577bVqfSoqKsTUqVOFra2t6N+/v7C2thYRERFCrVZr9TMzMxMAxNSpU4VKpWqqU6AWYO/evSIkJET06dNHABC7d++u1icxMVH4+PiInj17iokTJwpbW1vx3nvvabZHRkaKlStXiosXL4p58+aJ2bNni8zMzKY8DTJxH374ofDx8RHdu3cXAER+fn61Pjt27BA2NjZi9OjRYvjw4cLOzk7s3btX5/E+++wz0a9fv8YOm1oQlUollixZItq1ayfc3d1Fnz59dPZbv369sLa2Fr179xZubm5iwIABIisrSwghRHl5uTAzMxPz588XMplMdOrUSfz222+NFjMTJyM7deqU+O9//ysKCgpE165ddSZOH330kXBychIXL14UQghx9uxZYWVlJTZv3lytb3Z2tpgwYYJYv359o8dOLcf+/fvFX3/9JXJycmpMnEaOHCnCw8M1SfeOHTuERCIRf/31lxBCiMmTJwt/f3/h7+8vXFxchLOzs/jggw+a9DzItG3ZskXcvHlT7Ny5U2filJeXJ5ydncXbb7+taZszZ47w8vISZWVl1Y6XlZUl3NzcGj1uajnKysrEW2+9JVJSUsTcuXN1Jk7nzp0TUqlUfPvtt0KIyusuODhYTJs2TdPHyclJfPnll6K8vFz88ssvol27do0WM4fqjKx379547rnnYGtrW2Ofr776CpMmTUKnTp0AAD169MDYsWOxceNGAMDRo0exdu1aZGdno7CwEKWlpSgrK2uK8KmFGDFiBLp161bj9oyMDBw4cACzZs3SPFkyYcIEeHt747vvvgMAbNu2DUlJSUhKSsK8efMwZ84cLFiwoEnip5ZhypQp8PDwqHH7b7/9htzcXLzwwguattmzZ+PGjRs4dOgQAOD69etISkrC2bNnMXfuXAwdOrTR46aWw8LCAkuXLoWPj0+NfTZt2oR27drhscceAwDY29sjIiICW7ZsQUlJCQDggQceQGlpqebL3LzxpnAzcWpiarUaf//9N3r16qXV3qtXL8TGxgIA+vXrh9TUVHTp0gX33XcfOnbsiOeff745wqUWKi4uDkIIBAcHa9okEgmCgoJw/vz5av1dXFxYioDq7fz583B3d4ebm5umrVOnTrC0tNRcZ6+++irGjBmD6dOnw97eHp9++mlzhUst1NmzZ9GzZ0+ttl69eqGkpEQzj3jlypX44Ycf4O3tjSVLluCrr75qtHj4VF0TKyoqQllZWbUPKVdXV+Tk5ACozMCXL1+O5cuXN0eI1Ark5eUBqJyYezdnZ2dkZWVV6//iiy82RVjUyuTl5VW7xoDK667qGvzmm2+aOCpqbW7dulVt0rerqysAaD43PTw88MsvvzRJPLzj1MSqnl4qLi7Wai8qKuKTTWQ01tbWAID8/Hyt9ry8PNjY2DRHSNQKWVtbV7vGgMrrjtcZGYulpaXOz8yqbU2NiVMTs7S0hKenJ1JSUrTaU1NT0aFDh+YJilqdgIAAAJUlL+525coVzdw6ooYKCAhARkYGCgsLNW1paWkoLi7mdUZG06FDB52fmVXbmhoTp2YwZswY/PDDDxBCAKisX/Hjjz9izJgxzRwZtRbt27dH9+7d8b///U/TdvLkSVy4cAHjx49vxsioNRk9ejQkEgm2bdumadu8eTPs7OwwfPjwZoyMWpMxY8bgjz/+QFpamqbt+++/R/fu3eHp6dnk8XCOk5GVlZXhwIEDAICCggJcuXIFe/bsgZOTE0JDQwEAixYtQt++ffH4449j/Pjx2LJlC4qKivhEE+nt8uXL2Lt3r+b29a+//orU1FT07NlTc5199NFHGD16NIQQ8Pf3x6efforHHnsMw4YNa8bIqSU5cuQIzp07p3lw5fPPP4dMJsMDDzyAdu3awcPDA2+//TZeeuklJCQkoLy8HGvXrsWaNWtgb2/fzNFTSxETE4P8/HwkJycjLy8Pe/bsAQCEh4fDzMwMjz76KNasWYMHH3wQ8+bNw99//41Nmzbhp59+apZ4JaLqtgcZRU5ODv7v//6vWntQUBBWrVqleX3p0iWsWbMGycnJ8Pf3x7x58+Dr69uUoVILdubMGfz3v/+t1h4eHo5JkyZpXl+4cAH/+9//kJubiwEDBuDRRx/lkhekt61btyI6Orpae0REhFY5jAMHDuDXX3+FVCrFww8/jIEDBzZlmNTCzZo1C1evXq3WvmvXLlhZWQGovBGxevVqnDx5Ei4uLnj22Web7Tpj4kRERESkJ85xIiIiItITEyciIiIiPTFxIiIiItITEyciIiIiPTFxIiIiItITEyciIiIiPTFxIiIiItITEycialF27tyJa9euNXcY1aSlpeGnn37Cli1boKs83u7du3H58uVmiIyIjImJExG1KDNnzsTRo0ebOwwtf/75JwIDA7Fu3Trs2rVLZ+I0e/ZszXJMAPDjjz9WW4SZiEwfEyciogbavHkzwsPD8fPPP2PLli2QSqv/an3ooYfg7++vef3iiy/i4MGDTRkmERkBF/klonrZuXMn+vTpA5lMhrNnz8LOzg6hoaEwMzPT9NmyZQuGDx8OhUKhafvll18QEBCAgIAAreNYWFggNjYWtra2GDRoEKRSKZKTk3H27Fn4+vqid+/eOuO4ceMGYmNj4eDggIEDB1Zbg6+srAx//PEHcnNzERwcrJW03P3+EokEp0+fRrt27dCrVy+d75Wfn4+jR4+itLQU9913n9Z5/fzzzzh16hSsrKywZcsW+Pr6YtCgQdWOMWrUKLRv3x4A8Ntvv6G4uBh//vknrKysYGZmhkceeaRecRvyfTt9+jRSU1MREBCAoKAgnedKRHUQRET14OrqKkaPHi38/PzEuHHjhKenpxgyZIioqKjQ9AEg9u3bp7Vf586dxccff6x1nMGDB4uOHTuKcePGCWdnZxEWFibeffdd4e/vLx588EHh4OAg5s6dW+39w8PDhbe3txg7dqxwc3MTI0eOFCUlJZo+Z86cEe3btxe9evUS48aNE25ubuL555+vdpxRo0aJ9u3biwkTJoivvvpK5/lGRUUJJycn0adPH3H//fcLa2trsW7dOs32uXPninbt2gk/Pz8xZcoUsWrVKp3Had++vdiwYYMQQojXXntNWFtbi/79+4spU6aIp556ql5x1/f7VlJSIoYPHy58fX3Fww8/LEJCQsTEiROFWq3WGSsR1YyJExHVi6urq+jfv78oLCwUQgihVCqFra2t2LFjh6aPvolTaGioKC4uFkIIceLECQFADB06VJSWlgohhIiOjhZSqVTcvHlTa7/27duLjIwMIYQQN2/eFB4eHuLDDz8UQghRVlYmOnTooJXApKenC09PT/Htt99qHSckJETk5eXVeK7FxcXC19dXLFiwQNO2ceNGIZPJxOXLlzVtU6ZMETNmzKj1+3Z34iSEEN7e3uLLL7/UvK5P3PX9vv3000/C2dlZFBQUaI6za9cuoVKpao2ZiKrjHCciqrenn34aNjY2AAC5XI6goCBcuHCh3seZOnUqrKysAAB9+vSBpaUlpk2bBktLSwDAgAEDoFarkZSUpLXf9OnT4ebmBgDw8PDA1KlT8d133wEAoqOjkZycDLlcju3bt2Pbtm2Ijo6Gv78/oqOjqx3H3t6+xvhiYmKQkpKChQsXasUsl8uxc+fOep9vbeoTd32/b9bW1igtLdX6GT388MM652IRUe04x4mI6s3FxUXrtUwmQ0lJSb2P4+zsrPm3RCKBhYWFVptMJgOAasfu0KGD1uuOHTti06ZNAICrV6/C0tISP/74o1Yfb29vBAYGarV5enrWGl9ycjKcnJy0zlcikcDPzw/Jycl1nF391Cfu+n7fRowYgTlz5mDEiBFQKBQYOXIknnvuOfTs2dOo50DUFjBxIiKjk0gkUKvVWm2GJFY1ycnJqfa66g6Ug4MDysrK8Nlnn8HOzq7OOGvj5uaG/Px8VFRUwNz8zq/L7OxszfsZS33iNsSyZcvw9ttv49SpU/jf//6H/v3748yZM+jatavR34uoNeN9WiIyOm9vb63htUuXLiElJcVox9+1a5fm30II7Ny5U/Mk27Bhw2BpaYlPP/1Uax+VSgWlUlmv9+nXrx/Mzc3xww8/aNoSEhJw/vx5DB482PATAGBnZ6eVTBoz7nulpaVBpVLBwsICoaGh+Oijj2Bvb49Tp0416LhEbRHvOBGR0U2dOhVLlixBeXk5hBD47LPPYG1tbbTjnz9/HpMnT8aoUaPw008/ISkpCdu3bwdQOedp9erVmD17NhISEnDfffchNTUV33//Pf79739j3Lhxer+Pp6cn/vWvf2H69OlISEiAvb09Vq5ciQkTJmD48OENOoe+ffviiy++gI2NDaytrfHII48YLe57xcTE4O2338bkyZPRrl07REdHQyKRYMSIEQ06B6K2iHeciKhe/vGPf2jqEVUJCwtDSEiI5vU777yDZcuW4dy5c8jKysL333+P559/Xmuujq7jTJ48GT4+PlptU6ZMgYeHh9Z+O3bswKhRo3Dq1CkEBgbixIkTaNeunabPrFmzcPz4cbi4uODQoUMAgG3btmklH7reX5e33noL33zzDVJSUnD27FksXrwYW7du1eozcOBA9O/fv9bj3FsA86OPPsL48eOxf/9+7N69u0Fx1/V9mzRpEr799luUl5fj4MGD6NSpE86ePVttHyKqm0QIHWsDEBEREVE1vONEREREpCcmTkRERER6YuJEREREpCcmTkRERER6YuJEREREpCcmTkRERER6YuJEREREpCcmTkRERER6YuJEREREpCcmTkRERER6YuJEREREpCcmTkRERER6+n8DWTYFYtLtNwAAAABJRU5ErkJggg==",
      "text/plain": [
       "<Figure size 600x350 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import timeit\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "sizes = [1_000, 10_000, 100_000, 1_000_000]\n",
    "t_list, t_set = [], []\n",
    "for n in sizes:\n",
    "    data_list = list(range(n))\n",
    "    data_set = set(data_list)\n",
    "    target = n - 1                          # worst case for the list: the last element\n",
    "    t_list.append(timeit.timeit(lambda: target in data_list, number=50) / 50 * 1e6)\n",
    "    t_set.append(timeit.timeit(lambda: target in data_set, number=50) / 50 * 1e6)\n",
    "\n",
    "for n, a, b in zip(sizes, t_list, t_set):\n",
    "    print(f\"n={n:>9,}  list: {a:10.1f} µs   set: {b:6.2f} µs\")\n",
    "\n",
    "plt.figure(figsize=(6, 3.5))\n",
    "plt.loglog(sizes, t_list, \"o-\", label=\"list (O(n))\")\n",
    "plt.loglog(sizes, t_set, \"s-\", label=\"set (O(1))\")\n",
    "plt.xlabel(\"number of items\"); plt.ylabel(\"time per lookup (µs)\"); plt.legend(); plt.title(\"Membership test\")\n",
    "plt.tight_layout(); plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4872e00a",
   "metadata": {},
   "source": [
    "| Operation | list | tuple | dict | set |\n",
    "|---|---|---|---|---|\n",
    "| ordered | yes | yes | yes (insertion) | no |\n",
    "| mutable | yes | no | yes | yes |\n",
    "| duplicates | yes | yes | keys unique | no |\n",
    "| `x in c` | O(n) | O(n) | O(1) on keys | O(1) |\n",
    "| typical use | sequences | fixed records | lookups by key | membership, dedup |\n",
    "\n",
    "## Exercises\n",
    "1. Given a list of words, return the five most frequent with their counts (one line with `Counter`).\n",
    "2. Group a list of `(student, grade)` tuples into a dict `grade -> [students]` with `defaultdict`.\n",
    "3. Write a generator `fibonacci()` that yields the sequence forever, and print the first 15 values with `itertools.islice`.\n",
    "4. Repeat the timing experiment with a `dict` and a `tuple`. Which behaves like a list and which like a set?"
   ]
  }
 ],
 "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.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
