{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "internal-inside",
   "metadata": {},
   "source": [
    "<!--\n",
    "title: Variable Scope\n",
    "type: lesson\n",
    "duration: \"00:30\"\n",
    "creator: Brandi Butler\n",
    "-->\n",
    "\n",
    "<h1>Unit 3 Lab: Variable Scope</h1>\n",
    "\n",
    "<!--\n",
    "\n",
    "## Overview\n",
    "This lesson introduces local and global scope with a few examples. If there is time, give students examples of broken programs that mix up global and local scopes, and ask them to fix it.\n",
    "\n",
    "## Learning Objectives\n",
    "In this lesson, students will:\n",
    "\n",
    "* Define variable scope.\n",
    "* Explain the order of scope precedence that Python follows when resolving variable names.\n",
    "\n",
    "\n",
    "## Duration\n",
    "20 minutes\n",
    "\n",
    "## Suggested Agenda\n",
    "\n",
    "| Time | Activity |\n",
    "| --- | --- |\n",
    "| 0:00 - 0:03 | Welcome |\n",
    "| 0:03 - 0:08 | Local Scope |\n",
    "| 0:08 - 0:18 | Global scope |\n",
    "| 0:18 - 0:20 | Summary |\n",
    "\n",
    "## Differentiation and Extensions\n",
    "- There are no exercises involving classes, built-in scope, or enclosed scope. If there is time and your students seem confident, create some — or challenge your students to come up with examples themselves.\n",
    "\n",
    "## In Class: Materials\n",
    "- Projector\n",
    "- Internet connection\n",
    "- Python 3\n",
    "-->\n",
    "\n",
    "---\n",
    "\n",
    "## Lesson Objectives\n",
    "*After this lesson, you will be able to…*\n",
    "\n",
    "* Define variable scope.\n",
    "* Explain the order of scope precedence that Python follows when resolving variable names.\n",
    "\n",
    "---\n",
    "\n",
    "## Discussion: Delivering a Letter\n",
    "\n",
    "What if someone wanted to send Brandi a letter?\n",
    "\n",
    "If you just had \"For Brandi,\" the mail carrier would give the letter to the first Brandi they see!\n",
    "\n",
    "They'd look:\n",
    "\n",
    "- First in the class. Is there a \"Brandi\" here? They get the letter!\n",
    "- No? OK, look in the town. Is there a \"Brandi\" here? They get the letter!\n",
    "- No? OK, look in the state. Is there a \"Brandi\" here? They get the letter!\n",
    "\n",
    "---\n",
    "\n",
    "## Discussion: Your Address\n",
    "\n",
    "That's why **scope** matters. We might have to get more specific. To correctly deliver the letter, if the mail carrier only looked in the scope of:\n",
    "\n",
    "Your class:\n",
    "\n",
    "- You're probably the only Brandi.\n",
    "- \"For Brandi\" is fine.\n",
    "\n",
    "Your town:\n",
    "\n",
    "- There might be multiple Brandis in the town.\n",
    "- \"For Brandi, on Main Street\" is a bit more specific.\n",
    "\n",
    "In your state:\n",
    "\n",
    "- There are multiple Main Streets in New York!\n",
    "- \"For Brandi, on Main Street in Brooklyn\" is more specific.\n",
    "\n",
    "---\n",
    "\n",
    "## Discussion: What Is `x`?\n",
    "\n",
    "Python has **scope**, too. We can have many variables with the same name, and Python will look for the most specific one.\n",
    "\n",
    "In different scopes, you can reuse the same name. Each one is a *completely different* variable.\n",
    "\n",
    "Functions and classes create individual **local scopes**. A **local variable** doesn't exist outside its local function or class scope.\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "established-bubble",
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'x' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-1-d26609082d45>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      7\u001b[0m     \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mx\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;31m#5\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      8\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 9\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mx\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;31m# x is OUT OF SCOPE - no x exists here!\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mNameError\u001b[0m: name 'x' is not defined"
     ]
    }
   ],
   "source": [
    "def my_func1():\n",
    "    x = 1    # This is a LOCAL variable.\n",
    "    print(x) # 1\n",
    "\n",
    "def my_func2():\n",
    "    x = 5    # This is a DIFFERENT local variable.\n",
    "    print(x) #5\n",
    "\n",
    "print(x) # x is OUT OF SCOPE - no x exists here!\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "religious-rover",
   "metadata": {},
   "source": [
    "- Any variable declared or assigned inside of a function is local to that function.\n",
    "- Only the function in which the variable was declared has access to this scope — i.e., the variable is out of scope for everything but that function.\n",
    "\n",
    "---\n",
    "\n",
    "## Global Scope\n",
    "\n",
    "- Variables that are in **global scope** can be accessed by any function.\n",
    "- Python will adopt an 'inside-out' strategy when evaluating variable of the same name, giving precidence to a local variable before using a global one.\n",
    "- When we define a variable _inside_ a function, it's local by default.\n",
    "- When we defint a variable _outside_ a function, it's global by default.\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "joint-letters",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "5\n",
      "2\n"
     ]
    }
   ],
   "source": [
    "x = 2\n",
    "\n",
    "def my_func1():\n",
    "    x = 1\n",
    "    print(x) # 1 - Python checks local scopes first.\n",
    "\n",
    "def my_func2():\n",
    "    x = 5\n",
    "    print(x) # 5 - Python checks local scopes first.\n",
    "\n",
    "my_func1()\n",
    "my_func2()\n",
    "\n",
    "print(x) # 2 - Python found no local scope; prints global variable.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "miniature-belief",
   "metadata": {},
   "source": [
    "- Global variables are accessible from anywhere in the script. This is not necessarily a good thing, however, because those variables can be accessed, changed, or reassigned by anything, and this can lead to troublesome bugs.\n",
    "- Python assumes local unless otherwise specified.\n",
    "    * Meaning, these `x`s are three different variables.\n",
    "\n",
    "---\n",
    "\n",
    "## Multiple Variables, One Name\n",
    "\n",
    "Use case: `x` and `y` are frequently used to represent numbers.\n",
    "\n",
    "Scope is important so they don't interact!\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "artistic-happiness",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4.0\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def add(x, y):\n",
    "    return x + y\n",
    "\n",
    "def subtract(x, y):\n",
    "    return x - y\n",
    "\n",
    "def multiply(x, y):\n",
    "    return x * y\n",
    "\n",
    "def divide(x, y):\n",
    "    return x / y\n",
    "\n",
    "print(divide (8,2)) # Returns 4\n",
    "multiply(3,1) # Returns 3\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "enormous-defensive",
   "metadata": {},
   "source": [
    "\n",
    "---\n",
    "\n",
    "## We Do: Accessing Scopes\n",
    "\n",
    "Let's start with global scope:\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "charitable-holocaust",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5\n",
      "7\n"
     ]
    }
   ],
   "source": [
    "foo = 5\n",
    "print(foo)\n",
    "foo = 7\n",
    "print(foo)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "middle-pixel",
   "metadata": {},
   "source": [
    "\n",
    "---\n",
    "\n",
    "\n",
    "## We Do: Accessing Local Scope\n",
    "\n",
    "What if we add a variable in a local function scope and try to access it from the global scope?\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "oriented-dress",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5\n"
     ]
    },
    {
     "ename": "NameError",
     "evalue": "name 'bar' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-6-98b9e84cc5eb>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      8\u001b[0m \u001b[0mcoolFunc\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      9\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mfoo\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 10\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mbar\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mNameError\u001b[0m: name 'bar' is not defined"
     ]
    }
   ],
   "source": [
    "foo = 5\n",
    "\n",
    "def coolFunc():\n",
    "    bar = 8\n",
    "\n",
    "coolFunc()\n",
    "print(foo)\n",
    "print(bar)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "hungry-spice",
   "metadata": {},
   "source": [
    "\n",
    "It fails!\n",
    "\n",
    "- If you run this code, you will get an error: `NameError: name 'bar' is not defined.`.\n",
    "- The variable bar is only accessible from inside the `coolFunc()` function.\n",
    "- We called the `coolFunc()` function, but as soon as it finished running, the variable bar ceased to exist. Even while the function was running, it was only accessible to itself. But, `foo` in the global scope was still accessible.\n",
    "\n",
    "---\n",
    "\n",
    "## Scope Can Be Tricky\n",
    "\n",
    "What do you think happened here?\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "supreme-canada",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5\n",
      "6\n",
      "5\n"
     ]
    }
   ],
   "source": [
    "foo = 5\n",
    "def incrementFoo():\n",
    "    foo = 6\n",
    "    print(foo) # prints 6\n",
    "\n",
    "print(foo) # prints 5\n",
    "incrementFoo()\n",
    "print(foo) # prints 5\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "civilian-trading",
   "metadata": {},
   "source": [
    "- Hey! The variable `foo` went back to its old value after the function finished! Actually, not quite. Here's what happened:\n",
    "    - The line in the function where `foo` is assigned the value of `6` causes the creation of a new local variable.\n",
    "    - We then set this variable's value to `6`, the function prints the value, and the function finishes. However, the global variable `foo` was never touched by the function.\n",
    "\n",
    "---\n",
    "## The `global` Keyword\n",
    "\n",
    "You can force Python to reference a variable in the global scope by using the `global` keyword and the variable name."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "considerable-fireplace",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "8"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "foo = 5\n",
    "\n",
    "def my_func():\n",
    "    global foo\n",
    "    foo = 8\n",
    "\n",
    "my_func()\n",
    "foo"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "prospective-bailey",
   "metadata": {},
   "source": [
    "In practice, this is rarely seen as using function parameters and returns is generally a better way to go about this. For example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "public-engineer",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "8"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "foo = 5\n",
    "\n",
    "def my_func():\n",
    "    return 8\n",
    "\n",
    "foo = my_func()\n",
    "foo"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "prescription-student",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "## You Do: Just a Day in the Jungle\n",
    "\n",
    "* Declare a global variable `piranhas_hungry` and set it to `True`.\n",
    "* Write two functions, `swing_vine_over_river` and `jump_in_river`.\n",
    "* In `swing_vine_over_river`:\n",
    "  * Print `Ahhh! Piranhas got me!`.\n",
    "  * Change `piranhas_hungry` to `False`.\n",
    "* In `jump_in_river`:\n",
    "  * If `piranhas_hungry` is `True`:\n",
    "    * Print `I'm not going in there! There are hungry piranhas!`.\n",
    "  * Otherwise:\n",
    "    * Print `Piranhas are full! Swimming happily through the Amazon!`\n",
    "    \n",
    "Use either the `global` keyword or a return statement."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "delayed-accused",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "I'm not going in there! There are hungry piranhas!\n",
      "Ahhh! Piranhas got me!\n",
      "Piranhas are full! Swimming happily through the Amazon!\n"
     ]
    }
   ],
   "source": [
    "piranhas_hungry = True\n",
    "\n",
    "def jump_in_river():\n",
    "    if piranhas_hungry:\n",
    "        print(\"I'm not going in there! There are hungry piranhas!\")\n",
    "    else:\n",
    "        print(\"Piranhas are full! Swimming happily through the Amazon!\")\n",
    "\n",
    "def swing_vine_over_river():\n",
    "    global piranhas_hungry\n",
    "    print(\"Ahhh! Piranhas got me!\")\n",
    "    piranhas_hungry = False\n",
    "    \n",
    "# Call functions in this order.\n",
    "jump_in_river()\n",
    "swing_vine_over_river()\n",
    "jump_in_river()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "vanilla-shareware",
   "metadata": {},
   "source": [
    "**Expected output:**\n",
    "\n",
    "`I'm not going in there! There are hungry piranhas!`\n",
    "\n",
    "`Ahhh! Piranhas got me!`\n",
    "\n",
    "`Piranhas are full! Swimming happily through the Amazon!`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "strategic-exclusive",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "I'm not going in there! There are hungry piranhas!\n",
      "Ahhh! Piranhas got me!\n",
      "Piranhas are full! Swimming happily through the Amazon!\n"
     ]
    }
   ],
   "source": [
    "# Alternative method without global keyword\n",
    "def swing_vine_over_river():\n",
    "    print(\"Ahhh! Piranhas got me!\")\n",
    "    return False\n",
    "\n",
    "piranhas_hungry = True\n",
    "\n",
    "# Call functions in this order.\n",
    "jump_in_river()\n",
    "piranhas_hungry = swing_vine_over_river()\n",
    "jump_in_river()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "korean-injection",
   "metadata": {},
   "source": [
    "\n",
    "---\n",
    "\n",
    "## Summary and Q&A\n",
    "\n",
    "Python checks **scope** to find the right variable.\n",
    "\n",
    "- Functions and classes create individual **local scopes**.\n",
    "    * A `local` variable doesn't exist outside its local function or class scope.\n",
    "- Any variable declared or assigned outside of any function or class is considered \"global.\"\n",
    "    - Variables that are in **global scope** can be accessed anywhere.\n",
    "\n",
    "Python will check for a `local` variable before using a `global` one.\n",
    "\n",
    "There can be more levels. Python always works from the inside out — keep that in mind as your programs get more advanced!\n",
    "\n",
    "---\n",
    "\n",
    "## Additional Resources\n",
    "\n",
    "* [Global vs. Local Variables](https://www.python-course.eu/python3_global_vs_local_variables.php)\n",
    "* [Variables and Scope](http://python-textbok.readthedocs.io/en/1.0/Variables_and_Scope.html)\n",
    "* [Nested Functions — What Are They Good For?](https://realpython.com/inner-functions-what-are-they-good-for/)"
   ]
  }
 ],
 "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.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
