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.

615 lines
17 KiB

{
"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
}