CS30 - Spring 2016 - Class 7
Example code in this lecture
number_game.py
scope.py
Lecture notes
Administrative
- Assignment 1 grading issue
- if you lost points for "print" on the welcome function, e-mail me
- Assignment 3
run number_guessing_game in
number_game.py code
- picks a random number between 1 and 20 and you try and guess it
- keeps prompting you until you get it right
- gives you hints as to whether you need to guess higher or lower
- how could we implement this?
- pick a random number
- as long as (while) the user hasn't guessed the right answer
- get the guess from the user
- if it's the right answer
- print out "Good job!"
- somehow indicate that we're done looping
- otherwise, if the guess is too low
- print out higher
- otherwise (i.e. the guess must be too high)
- print out lower
bool variables
- just like any other variables except it's of type bool
- we've used variables to store ints, floats and strings
- this works the same way
- for example
>>> x = True
>>> x
True
>>> x = 10 < 0
>>> x
False
- we need some way of keeping track whether or not the user has guessed correctly or not
- we can us a bool variable and initially set it to some value
- condition the while loop on this variable
- change the value when we get it correct
look at number_guessing_game function in
number_game.py code
- use a variable called "finished" and initially set it to False
- could have also use a variable like "stillguessing" and set it to True and then had "while stillguessing:"
- when they get the number right we set finished = True and we will therefore exit the loop
- notice there are other ways of writing this function, e.g. number_guessing_game2
references
- objects reside in memory
- anytime we create a new int, float, string, list, etc. it is allocated in memory
- variables are references to objects in memory
- a variable does NOT hold the value itself, but it is a references to where that value resides
- arm/hand metaphor
- think of variables like your hands
- you can point them at things and then ask questions:
- what is the name of the thing that my right hand points to?
- for the thing that my left hand points to, do something?
- you can point them to new things (i.e. assignment)
- you can point point them at the same thing (i.e. assignment one to the other)
- but they are separate things!
- similarly, variables are separate things
- they can reference the same thing, but they are not the same thing
- when you ask for the value of a variable, it's actually getting the value of the things it references in memory
= (i.e. assignment)
- when we say x = y what we're actually saying is let x reference the same thing that y references
- x and y are still separate variables
- if we say x = z that does NOT change the value of y
mutable objects
- if an object is mutable (i.e. it has methods that change the object) then we have to be careful when we have multiple things referencing the same object (this is called "aliasing")
>>> x = [1, 2, 3, 4, 5]
>>> y = x
- what does this look like in memory?
- there is one list object
- both x and y are references to that same object (drawn as arrows in class)
- what happens when I call a method that changes/mutates the object?
>>> y.reverse()
>>> y
[5, 4, 3, 2, 1]
>>> x
[5, 4, 3, 2, 1]
x and y are references to the same object!
>>> x[0] = 0
>>> x
[0, 4, 3, 2, 1]
>>> y
[0, 4, 3, 2, 1]
statements that mutate the object that are done on either variable will affect this object
>>> y[0] = 15
>>> x
[15, 4, 3, 2, 1]
>>> y
[15, 4, 3, 2, 1]
- If we change what one of them references using assignment, then it will NOT affect the original object
>>> y = [0] * 5
>>> y
[0, 0, 0, 0, 0]
>>> x
[0, 4, 3, 2, 1]
there are now two separate objects and x and y each reference a different object
- why hasn't this problem come up with ints/floats/bools/strings? We said they're objects?
>>> x = 10
>>> y = x
- what does the memory picture look like?
- we just have one int object!
- x and y are both references to that one int object
- it seems like we could have the same problem as with lists...
- ints/floats/bools/strings are not mutable!
- there is no way for us to change the underlying object
- therefore, even though two variables reference the same int, it's as if they referenced separate ints
- draw the memory picture for the following:
>>> x = 10
>>> y = x
>>> y = 15
>>> x
10
>>> y
15
- we now have 2 objects (10 and 15)
- just like with lists, changing what y references does not affect x
parameter passing and references
- some terminology:
- when we define a functions 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?
def changer(a):
a[0] = 100
>>> 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
def no_changer(a):
a = [0]*5
>>> 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
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
- What would be printed out if we ran
scope.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 variablem x to -1
- After mystery1 returns we print out the values of the global variables x, y and z
- y and z are unchanged
- the global variable z was NOT changed mystery1, only the parameter z
- x *was* changed
- Why do programming languages limit a variable's scope?