= 7 x
Class 2
Variables and Functions
Objectives for today
- Describe the purpose of a variable and perform variable assignment
- Given a function definition, identify the name, function parameters and body
- Distinguish between global variables, function parameters and local variables
- Determine the return value of a function
- Use built-in functions such as
print
,input
,str
,int
, etc. - Implement simple functions
Assignment
Everything we have done so far was very ephemeral. We gain tremendous power by being able to save and use intermediate results. We do so by assigning values (the result of a expression) to a variable.
The assignment syntax is <variable> = <expression>
, e.g.,
Nothing was printed by the REPL. So did anything happen? To figure it out, lets build up our mental model of what is going on in the Python interpreter.
Assignment semantics:
- Evaluate the right-hand side (RHS) of the expression (all expressions in Python produce a value, and thus have a type)
- Store the resulting value in memory
- Set the variable name on the left-hand side (LHS) to point to the memory location with result Unlike expressions, the assignment statement does not evaluate to a value and thus nothing is printed to the REPL.
We can create these kind of diagrams automatically with Python Tutor.
What’s in a name?
Python variable names can generally be of any length, contain upper and lower case letters, digits and the underscore character (_
). Although variable names can contain digits, a variable name can’t start with a digit. Additionally there are a set of Python keywords (or “reserved” words) such as def
that have specific meaning in the Python language and so can’t be used as variable names. As you might imagine variable naming can get very contentious. We will talk a lot more about good “style”, of which variable naming is an important aspect, but in the meantime, aim for concise, descriptive variables names (e.g. “income” instead of “i” or “asdf”) that minimize the cognitive burden anyone reading your code.
Thonny mechanics (saving files, and finding errors)
If we close and restart Thonny and try to use variables we created before, we will get errors
x
--------------------------------------------------------------------------- NameError Traceback (most recent call last) Cell In[4], line 1 ----> 1 x NameError: name 'x' is not defined
We could repeat everything that we did, e.g.
= 4
x x
4
but that is very inefficient and can’t possibly be the “way”,
If we want to save our code, and use that code to regenerate results, we will need to create additional files. We can do so using the text editor panel in Thonny.
# An example script
= 7
x x
Everything to the right of #
(and including the #
) is a comment and is not executed. It is an explanatory note for the reader (which will often be you!).
I will create a script with our command, save the file, and then run it (with the green arrow). The result is the similar to if we had typed the same code into the interpreter, except we don’t get line-by-line feedback. That is we are not executing each line in the context of REPL (with the “print”) but just “read” and “eval”. We will need to use explicit print
functions to see the result.
# An example script
= 7
x print(x)
7
But we can use the variables we defined in the file later in the interpreter:
>>> x
7
Lets introduce an error so we see what Thonny reports, e.g. referencing an undefined variable y
:
>>> %Run class2.py
Traceback (most recent call last):
File "class2.py", line 3, in <module>
y
NameError: name 'y' is not defined
Functions
We have already used several functions, including print
and str
. These are “built” into Python.
= 7
x str(x + 7)
= str(x + 7)
y y
'14'
'14'
What happens when you call or invoke a function like str
. Python:
- Evaluates all the arguments, the expressions within the parentheses, from left to right
- Sets the function parameters to those values
- Then executes the body of the function, with the function call evaluating to the return value of the function (in the example, above, the return value is either printed by the REPL or assigned to the variable
y
)
Built-in functions are just the start.
Creating your own functions is a critical programming skill. Functions help us organize our code, and encapsulate computations (of any complexity) for reuse with different inputs.
Functions in Python (and other programming languages) are similar to their mathematical counterparts (which associates values of one set, the inputs, to another set, the outputs). For example you likely have seen this notation for functions:
\[f(x)=x^2\]
Let’s recreate that functionality (and function) in Python (**
is the power operator in Python).
3**2
4**2
9
16
We can implement the “squaring” with a function
To end a function you are directly typing in the shell (interpreter), hit enter on a blank line.
def f(x):
return x**2
I have now defined a function named f
. Note that nothing was executed. A function definition is just that, the definition of the computation a function should perform. It does not actually perform the computation. To do so we will need to invoke/call the function as we did above with str
.
Lets break down the function definition first:
def
is a keyword; it has a special meaning to Python, defining a function, and cannot be used as a variable or function name (there are approximately 30 such “reserved” keywords).f
is the function name (could be any valid identifier)- Everything inside the parentheses are the parameters, e.g.
x
. There can be zero or more parameters. These variables are only defined within the function body or scope. - I indent the body of the function, that is the code that the function will execute. This is how Python distinguishes the function body from other code.
- The
return
statement immediately terminates the function, and determines the value returned to the caller. If there is no return, the function terminates when it reaches the bottom (and returnsNone
, a special constant meaning the absence of a value). A function call evaluates to the function’s return value.
Now let’s invoke out newly defined function.
3)
f(4)
f(= f(5)
y
y4,5) f(
9
16
25
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[12], line 5 3 y = f(5) 4 y ----> 5 f(4,5) TypeError: f() takes 1 positional argument but 2 were given
Why did we get an error above? We tried to call f
with more parameters than specified in the function definition.
What is the current value of x
?
x
7
Surprised? Note that parameters and any variables defined inside the function scope don’t change variables in the enclosing scope. That is the function definition creates a new scope. A variable’s scope is the area of a program in which its value can be accessed. A function invocation creates a new frame (which the PP book calls a “namespace”) that contains instances of the function parameters and any local variables defined within the function.
This is a good spot for us to pause and formalize our mental model of how memory works in Python for assignment, re-assignment and functions. Let’s use a combination of recent examples.
Function writing: A recipe
Create a specification of the function along some examples of the function call, e.g.
“Return the string composed of first parameter, a space, and the second parameter”
>>> glue_strings('John', 'Smith') 'John Smith'
Define the type contract, i.e. the types of parameters the function accepts and the type it returns, e.g.
(str, str) -> str
Write the header
def glue_strings(first, second):
Implement the body
def glue_strings(first, second): return first + " " + second
Here is a more complex example for compound interest. Recall we can compute the value of savings earning interest yearly as
\[f(P, r, t) = P(1 + r)^t\]
where \(P\) is the principal, \(r\) is the rate and \(t\) is the time in years. How could we implement this in Python?
# Calculates investment value with interest compounded yearly
def compound_interest(principal, rate, years):
= principal * ((1 + rate) ** years)
amount return amount
Note the function body can be multiple lines. The comment (beginning with #
) is used to document to readers of the code (e.g., myself in the future) what the function does. Here we also define a “local variable” amount
. Much like parameters, local variables exist only within the scope of the function body.
And here are some more simple function examples.