Scope and
Pass By Reference
Jake Shoudy
Oct 28, 2022
CSCI 110 - Lecture 27
Announcements
Project 2: Part 2
More search engine things using 2D Lists :)
Due Sunday October 30th at 11:59pm
Project 1: Part 5 (Optional)
Multiplayer blackjack and score keeping.
Earn up to 50 points back on project 1 or 12.5 points on midterm 1.
Show Jake your code in person for grading
Due Monday October 31st
Final Exam Date?
OR
Everyone takes the test on Tuesday 11/29
Please vote on Ed ASAP
Recap
Review: Functions
def example_function(foo, bar, baz):
print(foo)
print(bar)
print(baz)
return foo
example_function("example", "text", "stuff")
Program: Max
Function: max()
Input: two numbers
Output: bigger of the two numbers
Examples:
max(1, 100) -> 100
max(-100, 0) -> 0
Calling Functions
result = get_max(1, 2)
other_result = get_max(-5, 5)
def get_max(a, b):
if a > b:
answer = a
else:
answer = b
return answer
Wasn’t defined yet!
NameError: name 'get_max' is not defined
Calling Functions
def max(a, b):
if a > b:
answer = a
else:
answer = b
return answer
result = max(1, 2)
other_result = max(-5, 5)
Call function to make it run.
Function definition must come before calling the function.
Flow of Execution
Order that code is run
Flow of Execution
def aFunction():
print("blue")
print("yellow")
aFunction()
print("green")
Flow of Execution
def aFunction():
print("blue")
print("yellow")
aFunction()
print("green")
Instructions are set up but have not been called yet, so this doesn't print.
Flow of Execution
def aFunction():
print("blue")
print("yellow")
aFunction()
print("green")
Prints “yellow”
Flow of Execution
def aFunction():
print("blue")
print("yellow")
aFunction()
print("green")
Calls the function!
Flow of Execution
def aFunction():
print("blue")
print("yellow")
aFunction()
print("green")
Function has been called! JUMP to function definition and execute code.
Flow of Execution
def aFunction():
print("blue")
print("yellow")
aFunction()
print("green")
Prints “blue”
Flow of Execution
def aFunction():
print("blue")
print("yellow")
aFunction()
print("green")
Once function is done, go back to where function was called, and continue.
Flow of Execution
def aFunction():
print("blue")
print("yellow")
aFunction()
print("green")
Prints “green”
Variable Scope
Scope
def wish_happy_birthday(name):
message = 'Happy birthday ' + name + '!'
return message
first_name = 'Mr Jake'
greeting = wish_happy_birthday(first_name)
print(greeting)
Happy birthday Mr Jake!
Scope
def wish_happy_birthday(name):
message = 'Happy birthday ' + name + '!'
return message
first_name = 'Mr Jake'
print(message)
greeting = wish_happy_birthday(first_name)
print(greeting)
NameError: name 'message' is not defined
Scope
def wish_happy_birthday(name):
message = 'Happy birthday ' + name + '!'
return message
first_name = 'Mr Jake'
greeting = wish_happy_birthday(first_name)
print(message)
print(greeting)
NameError: name 'message' is not defined
Scope
Where a variable is “visible”
2 types of scope:
global: can be used anywhere
local: can only be used in block of code where variable is defined
Parameters and variables defined in functions have local scope
When variable names are duplicated, look inside of the local scope first!
Scope Island
name
first_name
greeting
message
wish_happy_birthday
return
local
global
parameters
arguments
return value
Scope
def wish_happy_birthday(name):
message = 'Happy birthday ' + first_name + '!'
return message
first_name = 'Mr Jake'
greeting = wish_happy_birthday('Darth Vader')
print(greeting)
Happy birthday Mr Jake!
Scope
def wish_happy_birthday(name):
first_name = 'Mr Shoudy'
message = 'Happy birthday ' + first_name + '!'
return message
first_name = 'Mr Jake'
greeting = wish_happy_birthday('Darth Vader')
print(greeting)
Happy birthday Mr Shoudy!
Scope
def wish_happy_birthday(name):
message = 'Happy birthday ' + first_name + '!'
first_name = 'Mr Shoudy'
return message
first_name = 'Mr Jake'
greeting = wish_happy_birthday('Darth Vader')
print(greeting)
UnboundLocalError: local variable 'first_name' referenced before assignment
Local: Variables and Parameters
def addTwo(num):
num = num + 2
print("Inside the function, num = " + str(num))
return num
num = 8
print("Before the function, num = " + str(num))
new_num = addTwo(num)
print("After the function, num = " + str(num))
Before the function, num = 8
Inside the function, num = 10
After the function, num = 8
Pass by Value vs
Pass by Reference
Pass By Value vs Pass By Reference
Pass variable to function
Two ways of how computer actually passes information to function: pass by value and pass by reference.
Pass By Value
pass by value: make copy of value inside variable and pass that to function
Any operations inside function definition do not modify original variable
Pass By Value: Examples
def add_two(num): num = num + 2 return num digit = 5 result = add_two(digit) print(result) print(digit) |
def add_world(phrase): phrase = phrase + 'world' return phrase word = 'hello' result = add_world(word) print(result) print(word) |
7
5
helloworld
hello
Pass By Reference
pass by reference: pass pointer (place in computer memory) of variable to function
Any operations inside function definition do modify original variable
In Python, the variable type determines whether the argument will be pass-by-reference or pass-by-value:
Pointers
A pointer is a place in memory. In PythonTutor, they are represented by an arrow.
The variable (numbers) tells you where to find the list in memory.
Pass By Reference: List Examples
[1, 2, 3, 4, 2]
[1, 2, 3, 4, 2]
def add_two(nums): nums.append(2) return nums numbers = [1, 2, 3, 4] result = add_two(numbers) print(result) print(numbers) |
def remove_two(nums): nums.pop(2) return nums numbers = [1, 2, 3, 4] result = remove_two(numbers) print(result) print(numbers) |
[1, 3, 4]
[1, 3, 4]
Pass By Reference: Dictionary Examples
{'a': 3, 'b': 1, 'c': 2}
{'a': 3, 'b': 1, 'c': 2}
{'a': 3, 'b': 1}
{'a': 3, 'b': 1}
def add_c(mapping): mapping['c'] = 2 return mapping count = {'a': 3, 'b': 1} result = add_c(count) print(result) print(count) |
def remove_c(mapping): mapping.pop('c') return mapping count = {'a': 3, 'b': 1, 'c': 2} result = remove_two(count) print(result) print(count) |
Why does Pass-by-Reference Exist?
Why does this distinction exist? Why isn’t everything pass-by-value?
This is because lists, sets, dictionaries, and other mutable types can be large (thousands of elements), so it would be very expensive for the computer to make copies of them every time a function is called.
Pass By Reference: void functions
Pass by reference makes it possible to write void functions* that take a list or a dictionary as argument
Function has side effect: mutate list passed in as argument - but does not return value
* void functions are just functions that do not return (or explicitly return None)
Void Functions: List Example
[1, 2, 3, 4, 2]
[1, 2, 3, 4, 2]
def add_two(nums): nums.append(2) return nums numbers = [1, 2, 3, 4] result = add_two(numbers) print(numbers) |
def add_two(nums): nums.append(2) numbers = [1, 2, 3, 4] add_two(numbers) print(numbers) |
Void Functions: Dictionary Example
{'a': 3, 'b': 1}
{'a': 3, 'b': 1}
def remove_c(mapping): mapping.pop('c') return mapping count = {'a': 3, 'b': 1, 'c': 2} result = remove_two(count) print(count) |
def remove_c(mapping): mapping.pop('c') count = {'a': 3, 'b': 1, 'c': 2} remove_two(count) print(count) |
Void Functions: Dictionary Example
None
def remove_c(mapping): mapping.pop('c') count = {'a': 3, 'b': 1, 'c': 2} count = remove_c(count) print(count) |
Pass By Value vs
Pass By Reference
pass by value: passes a copy of the value the function
Modifying the parameter DOES NOT modify the argument
Default behavior for immutable types
pass by reference: passes a pointer to the object to the function
Modifying the parameter DOES modify the argument
Default behavior for mutable types
What if don’t want to change original?
def add_two(nums): nums.extend([2, 5]) return nums numbers = [1, 2, 3, 4] result = add_two(numbers) print(result) print(numbers) |
def add_two(nums): nums_copy = nums.copy() nums_copy.extend([2, 5]) return nums_copy numbers = [1, 2, 3, 4] result = add_two(numbers) print(result) print(numbers) |
What if don’t want to change original?
def double_value(mapping): for item in mapping: mapping[item] *= 2 return mapping count = {'a': 3, 'b': 1} result = double_value(count) print(result) print(count) |
def double_value(mapping): mapping_copy = mapping.copy() for item in mapping_copy: mapping_copy[item] *= 2 return mapping_copy count = {'a': 3, 'b': 1} result = double_value(count) print(result) print(count) |
Lists: + vs +=
Won’t be tested on this - but probably good to know for writing Python code
def add_ten(nums): nums = nums + [10] return nums numbers = [1, 2, 3] result = add_ten(numbers) print(result) print(numbers) |
def add_ten(nums): nums += [10] return nums numbers = [1, 2, 3] result = add_ten(numbers) print(result) print(numbers) |
[1, 2, 3, 10]
[1, 2, 3]
[1, 2, 3, 10]
[1, 2, 3, 10]
Let’s Code!
https://replit.com/team/csci110-01
Pass by Reference