{ "cells": [ { "cell_type": "markdown", "id": "decimal-finger", "metadata": {}, "source": [ "\n", "\n", "

Unit 3 Lab: Variable Scope

\n", "\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" ] }, { "cell_type": "code", "execution_count": null, "id": "distributed-definition", "metadata": {}, "outputs": [], "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!" ] }, { "cell_type": "markdown", "id": "british-celtic", "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": null, "id": "mechanical-going", "metadata": {}, "outputs": [], "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." ] }, { "cell_type": "markdown", "id": "improved-silver", "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": null, "id": "negative-arbitration", "metadata": {}, "outputs": [], "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" ] }, { "cell_type": "markdown", "id": "distinguished-vintage", "metadata": {}, "source": [ "\n", "---\n", "\n", "## We Do: Accessing Scopes\n", "\n", "Let's start with global scope:\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "id": "shaped-plane", "metadata": {}, "outputs": [], "source": [ "foo = 5\n", "print(foo)\n", "foo = 7\n", "print(foo)" ] }, { "cell_type": "markdown", "id": "collaborative-queens", "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": null, "id": "eligible-luxembourg", "metadata": {}, "outputs": [], "source": [ "foo = 5\n", "\n", "def coolFunc():\n", " bar = 8\n", "\n", "coolFunc()\n", "print(foo)\n", "print(bar)" ] }, { "cell_type": "markdown", "id": "genetic-capital", "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": null, "id": "thirty-carol", "metadata": {}, "outputs": [], "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" ] }, { "cell_type": "markdown", "id": "planned-knock", "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": null, "id": "lyric-liability", "metadata": {}, "outputs": [], "source": [ "foo = 5\n", "\n", "def my_func():\n", " global foo\n", " foo = 8\n", "\n", "my_func()\n", "foo" ] }, { "cell_type": "markdown", "id": "finnish-thunder", "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": null, "id": "reasonable-destination", "metadata": {}, "outputs": [], "source": [ "foo = 5\n", "\n", "def my_func():\n", " return 8\n", "\n", "foo = my_func()\n", "foo" ] }, { "cell_type": "markdown", "id": "velvet-prototype", "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": null, "id": "white-basketball", "metadata": {}, "outputs": [], "source": [ "\n", "\n", "\n", "\n", "# Call functions in this order.\n", "jump_in_river()\n", "swing_vine_over_river()\n", "jump_in_river()" ] }, { "cell_type": "markdown", "id": "pleased-million", "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!`\n", "\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/)\n" ] } ], "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 }