CS51A - Spring 2025 - Class 9
Example code in this lecture
parameter_passing.py
scope.py
scope2.py
debugging.py
debugging_with_prints.py
Lecture notes
admin
- Assignment 3
- Assignment 4
parameter passing and references
- some terminology:
- when we define a function's parameters we are defining the "formal parameters"
def my_function(a, b):
return a+b
- a and b are formal parameters
- until the function is actually called, they don't represent actual values
- when we call a function, the values that we give to the function are called the "actual parameters" (sometimes also called the arguments)
>>> x = 10
>>> y = 20
>>> my_function(x, y)
30
- the values 10 and 20 are the actual parameters (or similarly, x and y become the actual parameters)
- when a function is called the following happens:
- the values of the actual parameters are determined (we evaluate the expression representing each parameter)
- the value is just an object (could be an int, float, string, etc)
- these values are then "bound" or assigned to the formal parameters
- it's very similar to assignment
- the formal parameters represent new variables
- the formal parameters will then REFERENCE the same objects as those passed in through the actual parameters
- let's consider the picture for our function above
- x and y are both references to int objects
- when we call my_function, the formal parameters a and b, will represent two new variables
- these variables will reference the same thing as their actual parameters
- think of it like running the statements:
a = x
b = y
- a will reference the same thing as x
- b will reference the same thing as y
- for non-mutable objects, this whole story doesn't really matter. Why?
- they could be references to the same thing or copies, if we can't mutate the object, the behavior is the same
- how does this change for mutable objects?
- look at the changer function in
parameter_passing.py code
>>> x = [1, 2, 3, 4, 5]
>>> changer(x)
- what does the picture look like for this function call?
- x was a variable that references a list object
- when the function was called, the formal parameter a will represent a new variable
- a will reference the same thing as x
- think of it like running the statement
a = x
- because the object is mutable and since the formal parameter references the same object as what was passed in, changes made to the object referenced by a will also be seen in x
- notice, however, that operations that do not change/mutate the object will NOT be seen outside the function
- look at the no_changer function in
parameter_passing.py code
>>> x = [1, 2, 3, 4, 5]
>>> no_changer(x)
- in this case, we're assigning a to some new object
- we can't change what x references!
- x and a will no longer reference the same object
- any changes to a after this will not affect x
why is variable assignment and parameter passing done based on the references (i.e. a shallow copy) rather than a deep copy of the whole object?
- performance (it takes work to copy the object)
- often we just want the value and don't need to mutate the underlying object
What will be printed out when we run
scope.py code
?
- When we run the file, the first two functions will get defined
- Then, it will execute the three statements at the end of the file
- The first print statement will print out 20
- The second print statement will print out 30
- Why not 60? I.e., in double_input, we assigned 20 to val as the first line.
- val = 2*val updates the value of the parameter *NOT* the variable outside the function
The "scope" of a variable is the portion of the code we can reference that variable in and have it be valid
- When we declare a variable in the interpreter, what is its scope?
>>> x = 10
- the scope is any shell statements/expressions typed after it
- When we declare a variable (outside a function) in a file, what is its scope?
- anywhere below it in the file
- remember, running a program is very similar to typing those commands into the shell
- When we declare a variable inside a function, what is its scope?
- anywhere below it in the FUNCTION
- What is the scope of the parameters?
- anywhere in the FUNCTION
- it doesn't really make sense outside of the function since we wouldn't have a value for it
- Additionally, the scope also defines the context for a variable reference.
- What would be printed out if we ran
scope2.py code
?
- the program starts at the top and declares three "global" variables x, y and z
- then it declares two functions
- then it calls mystery1 with 10 and 20
- the parameter a gets the value 10
- the parameter z gets the value 20
- notice that this is *different* than the global variable z!
- in particular, when we execute z = 100, this reassigns the value of the local z, but not the global
- The program prints out s, x, y and z
- s is a local variable
- x and y are global
- z is the parameter
- and then sets the value of the global variable z to 100
- After mystery1 returns we print out the values of the global variables x, y and z
- all three are unchanged!
- x and y weren't changed anywhere
- z was changed in mystery1, but the z being changed was the parameter, not the global z
- What would happen if we added a call to mystery2 at the end and ran
scope2.py code
?
- Error. The scope of a is only defined within mystery1, so it cannot be accessed anywhere outside the function.
- Why do programming languages limit a variable's scope?
debugging
- The quatruple_input function in
debugging.py code
attempts to quadruple the inputs value by adding it four times
- However, if you run it, say with 5:
>>> quadruple_input(5)
6
we get the wrong answer
(In fact, no matter what you run it with, you always get the same answer!)
>>> quadruple_input(2)
6
>>> quadruple_input(10)
6
- A "bug" is a behavior in the code that is not intended. Debugging is the practice of trying to find and fix bugs.
- You might be able to look at the code and find the bug in this example. However, if you can't, you can try and add more information to your program to figure out what the problem is.
- Adding print statement is one good way to figure out what your function is doing
- Look at
debugging_with_prints.py code
- If we run this version, we start to see what the problem is:
>>> quadruple_input(5)
before loop item is: 5
item is: 0
total is: 0
item is: 1
total is: 0
item is: 2
total is: 1
item is: 3
total is: 3
after loop item is: 3
6
- The problem is that we've used the input parameter as the variable in the for loop and the value is getting lost!
- The fix is to use a different variable name here (e.g., 'i')
- When you're all done debugging and your code works, make sure to remove the print statements!
string methods
- a few that might be useful (though there are many there)
- lower
>>> s = "Banana"
>>> s.lower()
'banana'
>>> s
'Banana'
- Remember, strings are immutable!
- replace
>>> s.replace("a", "o")
'Bonono'
>>> s
'Banana'
- remember that strings are immutable
- if you want to update a variable after the method has been applied, you can do the following:
>>> s
'Banana'
>>> s = s.replace("a", "o")
>>> s
'Bonono'
- find
>>> s.find("a")
1
>>> s.find("q")
-1
>>> s.find("a", 2)
3
- count
>>> s.count("a")
3
>>> s.count("b")
0
write a function find_letter(string, letter) which returns the index of the first occurrence of letter in string (or -1 if it doesn't occur in the string).
def find_letter(str, letter):
for i in range(length(str)):
char = str[i]
if char == letter:
return i
return -1