{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "6b4fce18-128e-441d-9040-7d738b1ac1c8",
   "metadata": {},
   "source": [
    "## PDSP 2025, Lecture 11, 11 September 2025"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b9a5d845",
   "metadata": {},
   "source": [
    "### Scope and global variables\n",
    "- The scope of a variable refers to the portion of the program where its value is available\n",
    "- If we refer to a value that is not defined in a function, it is looked up in the global context"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "02be75d4",
   "metadata": {},
   "outputs": [],
   "source": [
    "def f():\n",
    "    y = x + 22\n",
    "    print(y)\n",
    "    return"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "14c15030-85f1-4514-bb64-36695d52d525",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "29\n"
     ]
    }
   ],
   "source": [
    "x = 7\n",
    "f()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "009e036a",
   "metadata": {},
   "source": [
    "- As soon as we assign a variable a value inside a function, *all* instances of that variable are treated as local to the function\n",
    "- This decision is *static* based on the program text. In the code below, we cannot be sure that the assignment `x = 33` will execute, but Python still denotes `x` to be local to `f()`\n",
    "- Though the check is based on the static program text, the error is flagged only when the function executes. The definition of `f()` does not trigger an error though the problem is evident in the text of the function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "297f4b25",
   "metadata": {},
   "outputs": [],
   "source": [
    "def f():\n",
    "    y = x + 22\n",
    "    print(y)\n",
    "    if y > 1000:\n",
    "        x = 33\n",
    "    return\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "f1d4f69e-b6a2-4c30-b3c8-2cc338f69197",
   "metadata": {},
   "outputs": [
    {
     "ename": "UnboundLocalError",
     "evalue": "cannot access local variable 'x' where it is not associated with a value",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mUnboundLocalError\u001b[39m                         Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[4]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m      1\u001b[39m x = \u001b[32m7\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m \u001b[43mf\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[3]\u001b[39m\u001b[32m, line 2\u001b[39m, in \u001b[36mf\u001b[39m\u001b[34m()\u001b[39m\n\u001b[32m      1\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mf\u001b[39m():\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m     y = \u001b[43mx\u001b[49m + \u001b[32m22\u001b[39m\n\u001b[32m      3\u001b[39m     \u001b[38;5;28mprint\u001b[39m(y)\n\u001b[32m      4\u001b[39m     \u001b[38;5;28;01mif\u001b[39;00m y > \u001b[32m1000\u001b[39m:\n",
      "\u001b[31mUnboundLocalError\u001b[39m: cannot access local variable 'x' where it is not associated with a value"
     ]
    }
   ],
   "source": [
    "x = 7\n",
    "f()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "42ede9fd-381a-4ece-a68a-af16f5ce438f",
   "metadata": {},
   "source": [
    "- This static check applies even if it is impossible for the local assignment to be executed"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "52df9392-f73a-47ab-87ec-5669bb6af3fd",
   "metadata": {},
   "outputs": [],
   "source": [
    "def checky():\n",
    "    y = x + 2\n",
    "    return\n",
    "    if False:\n",
    "        x = 7"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "4138cdc7-aa48-4f26-87d8-911b5ad6349a",
   "metadata": {},
   "outputs": [
    {
     "ename": "UnboundLocalError",
     "evalue": "cannot access local variable 'x' where it is not associated with a value",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mUnboundLocalError\u001b[39m                         Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[6]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m      1\u001b[39m x = \u001b[32m8\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m \u001b[43mchecky\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[5]\u001b[39m\u001b[32m, line 2\u001b[39m, in \u001b[36mchecky\u001b[39m\u001b[34m()\u001b[39m\n\u001b[32m      1\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mchecky\u001b[39m():\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m     y = \u001b[43mx\u001b[49m + \u001b[32m2\u001b[39m\n\u001b[32m      3\u001b[39m     \u001b[38;5;28;01mreturn\u001b[39;00m\n\u001b[32m      4\u001b[39m     \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28;01mFalse\u001b[39;00m:\n",
      "\u001b[31mUnboundLocalError\u001b[39m: cannot access local variable 'x' where it is not associated with a value"
     ]
    }
   ],
   "source": [
    "x = 8\n",
    "checky()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5b7be1d2",
   "metadata": {},
   "source": [
    "- More examples of using global values within a function without redefining the variable "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "570ee35e",
   "metadata": {},
   "outputs": [],
   "source": [
    "def display_count():\n",
    "    print(count)\n",
    "    return"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "670190c4",
   "metadata": {},
   "outputs": [],
   "source": [
    "def display_upto_count():\n",
    "    for i in range(count):\n",
    "        print(count+i)\n",
    "    return"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b7c76b7d-a5bc-43fd-85ba-371d3650a939",
   "metadata": {},
   "source": [
    "- If we call `display_count()` without a global definition for `count` we get an error"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "61b3ed37-648b-429d-a63e-6353aeaf739a",
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'count' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mNameError\u001b[39m                                 Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[9]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[43mdisplay_count\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[7]\u001b[39m\u001b[32m, line 2\u001b[39m, in \u001b[36mdisplay_count\u001b[39m\u001b[34m()\u001b[39m\n\u001b[32m      1\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mdisplay_count\u001b[39m():\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m     \u001b[38;5;28mprint\u001b[39m(\u001b[43mcount\u001b[49m)\n\u001b[32m      3\u001b[39m     \u001b[38;5;28;01mreturn\u001b[39;00m\n",
      "\u001b[31mNameError\u001b[39m: name 'count' is not defined"
     ]
    }
   ],
   "source": [
    "display_count()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a4ef316b-2b46-412b-857a-a11e54d56fd7",
   "metadata": {},
   "source": [
    "- If `count` is available in the global context, the two functions work as expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "e3f0d201",
   "metadata": {},
   "outputs": [],
   "source": [
    "count = 7"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "746b1e0b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "7\n"
     ]
    }
   ],
   "source": [
    "display_count()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "8121a41a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "7\n",
      "8\n",
      "9\n",
      "10\n",
      "11\n",
      "12\n",
      "13\n"
     ]
    }
   ],
   "source": [
    "display_upto_count()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e5fb13a0",
   "metadata": {},
   "source": [
    "- If we try to update `count` inside the function, both occurrences become local\n",
    "- The occurrence on the right hand side of the assignment generates an error because its value is now undefined\n",
    "- Once again, this *static* error is only triggered at run-time when the function executes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "1523eab6",
   "metadata": {},
   "outputs": [],
   "source": [
    "def increment_local(k):\n",
    "    count = count+k\n",
    "    return"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "2ab211d9",
   "metadata": {},
   "outputs": [
    {
     "ename": "UnboundLocalError",
     "evalue": "cannot access local variable 'count' where it is not associated with a value",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mUnboundLocalError\u001b[39m                         Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[14]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[43mincrement_local\u001b[49m\u001b[43m(\u001b[49m\u001b[32;43m2\u001b[39;49m\u001b[43m)\u001b[49m\n",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[13]\u001b[39m\u001b[32m, line 2\u001b[39m, in \u001b[36mincrement_local\u001b[39m\u001b[34m(k)\u001b[39m\n\u001b[32m      1\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mincrement_local\u001b[39m(k):\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m     count = \u001b[43mcount\u001b[49m+k\n\u001b[32m      3\u001b[39m     \u001b[38;5;28;01mreturn\u001b[39;00m\n",
      "\u001b[31mUnboundLocalError\u001b[39m: cannot access local variable 'count' where it is not associated with a value"
     ]
    }
   ],
   "source": [
    "increment_local(2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "862d2351",
   "metadata": {},
   "source": [
    "- Reassigning a variable within a function disconnects it from the external variable with the same name"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "232ca581",
   "metadata": {},
   "outputs": [],
   "source": [
    "def reset_local(k):\n",
    "    count = k\n",
    "    return"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "6200fc0e",
   "metadata": {},
   "outputs": [],
   "source": [
    "reset_local(77)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "e595dd1d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "7"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "count"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ca7a5faa",
   "metadata": {},
   "source": [
    "- We can declare a variable to be `global` to override Python's default scope rules\n",
    "- `global` tells Python to treat the variable inside the function as one from the global context"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "83036984",
   "metadata": {},
   "outputs": [],
   "source": [
    "def increment_global(k):\n",
    "    global count\n",
    "    count = count+k\n",
    "    return"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "98ed11e8",
   "metadata": {},
   "outputs": [],
   "source": [
    "increment_global(8)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "3e952ab2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "15\n"
     ]
    }
   ],
   "source": [
    "display_count()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c9300f2a",
   "metadata": {},
   "source": [
    "- The default rule about local scope applies to mutable values as well"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "5ddd419b",
   "metadata": {},
   "outputs": [],
   "source": [
    "def concat_local():\n",
    "    l1 = l1 + l2\n",
    "    return"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "cd804009",
   "metadata": {},
   "outputs": [
    {
     "ename": "UnboundLocalError",
     "evalue": "cannot access local variable 'l1' where it is not associated with a value",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mUnboundLocalError\u001b[39m                         Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[22]\u001b[39m\u001b[32m, line 3\u001b[39m\n\u001b[32m      1\u001b[39m l1 = [\u001b[32m1\u001b[39m,\u001b[32m2\u001b[39m,\u001b[32m3\u001b[39m]\n\u001b[32m      2\u001b[39m l2 = [\u001b[32m4\u001b[39m,\u001b[32m5\u001b[39m,\u001b[32m6\u001b[39m]\n\u001b[32m----> \u001b[39m\u001b[32m3\u001b[39m \u001b[43mconcat_local\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[21]\u001b[39m\u001b[32m, line 2\u001b[39m, in \u001b[36mconcat_local\u001b[39m\u001b[34m()\u001b[39m\n\u001b[32m      1\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mconcat_local\u001b[39m():\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m     l1 = \u001b[43ml1\u001b[49m + l2\n\u001b[32m      3\u001b[39m     \u001b[38;5;28;01mreturn\u001b[39;00m\n",
      "\u001b[31mUnboundLocalError\u001b[39m: cannot access local variable 'l1' where it is not associated with a value"
     ]
    }
   ],
   "source": [
    "l1 = [1,2,3]\n",
    "l2 = [4,5,6]\n",
    "concat_local()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "8e2ce199",
   "metadata": {},
   "outputs": [],
   "source": [
    "def concat_global():\n",
    "    global l1\n",
    "    l1 = l1 + l2\n",
    "    return"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "5f839b8e",
   "metadata": {},
   "outputs": [],
   "source": [
    "l1 = [1,2,3]\n",
    "l2 = [4,5,6]\n",
    "concat_global()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "6209c49d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "([1, 2, 3, 4, 5, 6], [4, 5, 6])"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "l1, l2"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "872daf81",
   "metadata": {},
   "source": [
    "- We can define a value inside a function and \"export\" it outside by declaring it `global`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "c48cc401",
   "metadata": {},
   "outputs": [],
   "source": [
    "del(l1)\n",
    "del(l2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "dace6789",
   "metadata": {},
   "outputs": [],
   "source": [
    "def concat_global():\n",
    "    global l1\n",
    "    l1 = [1,2,3]\n",
    "    l1 = l1 + l2\n",
    "    return"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "41cacace",
   "metadata": {},
   "outputs": [],
   "source": [
    "l2 = [4,5,6]\n",
    "concat_global()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "875e3b0a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[1, 2, 3, 4, 5, 6]"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "l1"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2aa568d8-8825-4627-9c39-9b49db81793d",
   "metadata": {},
   "source": [
    "- The following would work with *dynamic* scoping -- based on execution of program\n",
    "    - `init()` defines `b` and then calls `seta()`, so with dynamic scoping, `b` is known to `seta()`\n",
    "- Python uses *static* scoping -- based on text of program -- so this code generates an error\n",
    "- Most languages use static scoping because dynamic scoping makes it hard to reason about correctness"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "a5cdc366-db8c-41ea-9622-488b2a2348fc",
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'b' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mNameError\u001b[39m                                 Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[30]\u001b[39m\u001b[32m, line 9\u001b[39m\n\u001b[32m      6\u001b[39m     b = \u001b[32m7\u001b[39m\n\u001b[32m      7\u001b[39m     seta()\n\u001b[32m----> \u001b[39m\u001b[32m9\u001b[39m \u001b[43minit\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[30]\u001b[39m\u001b[32m, line 7\u001b[39m, in \u001b[36minit\u001b[39m\u001b[34m()\u001b[39m\n\u001b[32m      5\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34minit\u001b[39m():\n\u001b[32m      6\u001b[39m     b = \u001b[32m7\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m7\u001b[39m     \u001b[43mseta\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[30]\u001b[39m\u001b[32m, line 2\u001b[39m, in \u001b[36mseta\u001b[39m\u001b[34m()\u001b[39m\n\u001b[32m      1\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mseta\u001b[39m():\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m     a = \u001b[43mb\u001b[49m + \u001b[32m5\u001b[39m\n\u001b[32m      3\u001b[39m     \u001b[38;5;28mprint\u001b[39m(a)\n",
      "\u001b[31mNameError\u001b[39m: name 'b' is not defined"
     ]
    }
   ],
   "source": [
    "def seta():\n",
    "    a = b + 5\n",
    "    print(a)\n",
    "\n",
    "def init():\n",
    "    b = 7\n",
    "    seta()\n",
    "\n",
    "init()"
   ]
  }
 ],
 "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.13.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
