{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "dd1b7deb-a3e3-4a5d-858c-cda13f2452ce",
   "metadata": {
    "tags": []
   },
   "source": [
    "## Scope\n",
    "The location of a declaration determines the scope of where it is accessible via code.\n",
    "\n",
    "We've dealt with two types of scope so far:\n",
    "\n",
    "- module scope (Global)\n",
    "- function scope (Local)\n",
    "\n",
    "Anything declared inside of a function, including its parameter names, are considered local.\n",
    "\n",
    "### Scope Rules\n",
    "\n",
    "Assignment statements create or change local names by default.\n",
    "\n",
    "Referencing a name follows LEGB:\n",
    "\n",
    "    - Local: Scope of the function.\n",
    "    - Enclosing: Scope of any enclosing functions.\n",
    "    - Global: Scope of the file.\n",
    "    - Built-in: Built-ins.\n",
    "\n",
    "If none are found, an exception is raised.\n",
    "\n",
    "#### `global` and `nonlocal`\n",
    "\n",
    "Allow us to modify variables in non-local scopes.\n",
    "\n",
    "Minimize use, as they make code harder to follow."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bf892e39-64d4-417c-92dc-353b396c60c9",
   "metadata": {},
   "outputs": [],
   "source": [
    "RED = (255, 0, 0)\n",
    "\n",
    "def paint(color):\n",
    "    coordinate = (100, 100)\n",
    "    ...\n",
    "\n",
    "paint(RED)\n",
    "\n",
    "# How many globals? \n",
    "# How many locals inside paint?\n",
    "\n",
    "# where does print come from?\n",
    "# list, set, dict, int, str, etc."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "d2a780bf-44bd-47f5-9de6-5ba905e8f4f3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "abs\n",
      "aiter\n",
      "all\n",
      "anext\n",
      "any\n",
      "ascii\n",
      "bin\n",
      "bool\n",
      "breakpoint\n",
      "bytearray\n",
      "bytes\n",
      "callable\n",
      "chr\n",
      "classmethod\n",
      "compile\n",
      "complex\n",
      "copyright\n",
      "credits\n",
      "delattr\n",
      "dict\n",
      "dir\n",
      "display\n",
      "divmod\n",
      "enumerate\n",
      "eval\n",
      "exec\n",
      "execfile\n",
      "filter\n",
      "float\n",
      "format\n",
      "frozenset\n",
      "get_ipython\n",
      "getattr\n",
      "globals\n",
      "hasattr\n",
      "hash\n",
      "help\n",
      "hex\n",
      "id\n",
      "input\n",
      "int\n",
      "isinstance\n",
      "issubclass\n",
      "iter\n",
      "len\n",
      "license\n",
      "list\n",
      "locals\n",
      "map\n",
      "max\n",
      "memoryview\n",
      "min\n",
      "next\n",
      "object\n",
      "oct\n",
      "open\n",
      "ord\n",
      "pow\n",
      "print\n",
      "property\n",
      "range\n",
      "repr\n",
      "reversed\n",
      "round\n",
      "runfile\n",
      "set\n",
      "setattr\n",
      "slice\n",
      "sorted\n",
      "staticmethod\n",
      "str\n",
      "sum\n",
      "super\n",
      "tuple\n",
      "type\n",
      "vars\n",
      "zip\n"
     ]
    }
   ],
   "source": [
    "for name in dir(__builtins__):\n",
    "    if name[0].islower():\n",
    "        print(name)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7beecb5d-3cb8-42e3-ad94-689eebf603bc",
   "metadata": {},
   "outputs": [],
   "source": [
    "## Without global declaration \n",
    "x = 2\n",
    "def f():\n",
    "    # x = 2\n",
    "    x += 1\n",
    "    print(x) # prints: 3\n",
    "f() \n",
    "print(x) # global scope x was not modified"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "74a02260-838d-43d6-ab6e-c38d13410346",
   "metadata": {},
   "outputs": [
    {
     "ename": "UnboundLocalError",
     "evalue": "local variable 'x' referenced before assignment",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mUnboundLocalError\u001b[0m                         Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[7], line 10\u001b[0m\n\u001b[1;32m      8\u001b[0m     x \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m5\u001b[39m\n\u001b[1;32m      9\u001b[0m     \u001b[38;5;28mprint\u001b[39m(y) \n\u001b[0;32m---> 10\u001b[0m \u001b[43mf\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m \n\u001b[1;32m     11\u001b[0m \u001b[38;5;28mprint\u001b[39m(x) \u001b[38;5;66;03m# global scope x was modified\u001b[39;00m\n",
      "Cell \u001b[0;32mIn[7], line 7\u001b[0m, in \u001b[0;36mf\u001b[0;34m()\u001b[0m\n\u001b[1;32m      3\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mf\u001b[39m():\n\u001b[1;32m      4\u001b[0m     \u001b[38;5;66;03m#global x\u001b[39;00m\n\u001b[1;32m      5\u001b[0m     \u001b[38;5;66;03m#x = 1\u001b[39;00m\n\u001b[1;32m      6\u001b[0m     \u001b[38;5;66;03m#x += 1\u001b[39;00m\n\u001b[0;32m----> 7\u001b[0m     y \u001b[38;5;241m=\u001b[39m \u001b[43mx\u001b[49m\n\u001b[1;32m      8\u001b[0m     x \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m5\u001b[39m\n\u001b[1;32m      9\u001b[0m     \u001b[38;5;28mprint\u001b[39m(y)\n",
      "\u001b[0;31mUnboundLocalError\u001b[0m: local variable 'x' referenced before assignment"
     ]
    }
   ],
   "source": [
    "## With global declaration \n",
    "x = 5\n",
    "def f():\n",
    "    #global x\n",
    "    #x = 1\n",
    "    #x += 1\n",
    "    y = x\n",
    "    x = 5\n",
    "    print(y) \n",
    "f() \n",
    "print(x) # global scope x was modified"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7ee84621-6624-4357-ac95-6ba3419592b3",
   "metadata": {},
   "source": [
    "### Nested Functions\n",
    "\n",
    "We've seen an example before, we can define functions within functions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "8cdea7b4-8b72-431f-b6c1-a7756eb3c451",
   "metadata": {},
   "outputs": [],
   "source": [
    "def f1():\n",
    "    x = \"OUTER\" \n",
    "    def f2():\n",
    "        nonlocal x\n",
    "        x = \"INNER\"\n",
    "        print(\"inside f2 x=\", x)\n",
    "    print(\"inside f1 before f2 has been called x=\", x)\n",
    "    f2()\n",
    "    print(\"inside f1 after f2 has been called\", x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "03894ba5-f23d-4de3-80ff-08422952ffd5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "inside f1 before f2 has been called x= OUTER\n",
      "inside f2 x= INNER\n",
      "inside f1 after f2 has been called INNER\n"
     ]
    }
   ],
   "source": [
    "inner_func = f1()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "60faa092-ee97-4c43-aa0a-64e607e5d432",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "inner_func()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "21531a6f-cb70-4ca9-b030-7928487b71d7",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def create_counter_func():\n",
    "    counter = 0\n",
    "    def f():\n",
    "        nonlocal counter\n",
    "        counter += 1\n",
    "        print(f\"called {counter} times\")\n",
    "    return f\n",
    "\n",
    "g = create_counter_func()\n",
    "h = create_counter_func()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "1e486a39-efa4-47eb-bdf2-7bd2038c37f8",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "called 5 times\n"
     ]
    }
   ],
   "source": [
    "h()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "9f12ec73-ee3e-4ed6-8c7c-603cd8238dec",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "called 5 times\n"
     ]
    }
   ],
   "source": [
    "g()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1e0c52aa-08bc-4482-b205-c2469d52fc5f",
   "metadata": {},
   "source": [
    "#### Closures\n",
    "\n",
    "When a function is nested inside another function, it remembers the enclosing scope for our LEGB lookup.\n",
    "\n",
    "The combination of a nested function and its enclosing scope is called a closure."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "43fa84fb-f7c4-4b46-a882-d2d31b1a23f5",
   "metadata": {},
   "outputs": [],
   "source": [
    "def make_func(n):\n",
    "    def f(x):\n",
    "        # n: locally scoped to make_func() < enclosing scope\n",
    "        # x: locally scoped to f()\n",
    "        # we are using the n from the enclosing scope\n",
    "        return x ** n \n",
    "    return f"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "acaf5a72-aac2-4fd4-aa39-4423998fca68",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1000"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "to_the_third = make_func(3)\n",
    "to_the_third(10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4c16d356-8c0c-4fc7-a6ff-bbab09a56006",
   "metadata": {},
   "outputs": [],
   "source": [
    "squared = make_func(2)\n",
    "squared(55)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1d4fc6ca-cd11-446c-9530-c85bc09cc34c",
   "metadata": {},
   "outputs": [],
   "source": [
    "squared(10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3546e348-e3a6-4516-a14a-fc2cf63e8167",
   "metadata": {},
   "outputs": [],
   "source": [
    "import math\n",
    "\n",
    "def make_cached_calc():\n",
    "    prior_calls = {}\n",
    "    \n",
    "    def calc(x, y):\n",
    "        if (x, y) not in prior_calls:\n",
    "            print(f\"doing computation on {x} and {y}...\")\n",
    "            # do computation\n",
    "            answer = math.sin(x) + math.exp(y)\n",
    "            # save to cache\n",
    "            prior_calls[x, y] = answer\n",
    "\n",
    "        print(\"cache=\", prior_calls)\n",
    "        # retrieve from cache\n",
    "        return prior_calls[x, y]\n",
    "    \n",
    "    return calc\n",
    "\n",
    "do_computation = make_cached_calc()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e0f5f84f-e091-4096-a45f-002dd74af8c5",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "eebb454b-7414-46a6-8a08-f02cca13ed76",
   "metadata": {},
   "outputs": [],
   "source": [
    "do_computation(1, 2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "50ac829c-0ca5-433b-a2ec-6c0bde23ee3f",
   "metadata": {},
   "outputs": [],
   "source": [
    "do_computation(1, 2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4f9ce6dd-2d77-431d-9f76-f5a175dd47f4",
   "metadata": {},
   "outputs": [],
   "source": [
    "do_computation(0.5, 0.7)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "848c513c-5d15-451a-afb2-5ff24fb7c7f2",
   "metadata": {},
   "outputs": [],
   "source": [
    "do_computation2 = make_cached_calc()\n",
    "do_computation2(1, 2)"
   ]
  },
  {
   "attachments": {
    "scope.png": {
     "image/png": "[encoded data, 303272 characters]"
    }
   },
   "cell_type": "markdown",
   "id": "e1f9b234-91f2-4dbd-92aa-cd765841c98e",
   "metadata": {},
   "source": [
    "![scope.png](attachment:scope.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "62a089f9-4ec4-48d9-9720-6f0d276b665f",
   "metadata": {},
   "source": [
    "-- Learning Python, 2013"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dcb358e3-ed34-4f8c-8afe-c25bc6472eda",
   "metadata": {},
   "source": []
  }
 ],
 "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"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
