You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

458 lines
13 KiB

{
"cells": [
{
"cell_type": "markdown",
"id": "decimal-finger",
"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"
]
},
{
"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
}