Lab 01 - Variables, Functions, and Control
Starter Files
Download lab01.zip. Inside the archive, you will find starter files for the questions in this lab.
BYU Laptop Rentals Website
Logistics Review
Using Python
When running a Python file, you can use options on the command line to inspect your code further. Here are a few that will come in handy. If you want to learn more about other Python command-line options, take a look at the documentation.
-
Using no command-line options will run the code in the file you provide and return you to the command line. For example, if we want to run
lab01.py
this way, we would write in the terminal:python3 lab01.py
-
-i
: The-i
option runs your Python script, then opens an interactive session. In an interactive session, you run Python code line by line and get immediate feedback instead of running an entire file all at once. To exit, typeexit()
into the interpreter prompt. You can also use the keyboard shortcutCtrl-D
on Linux/Mac machines orCtrl-Z Enter
on Windows.If you edit the Python file while running it interactively, you will need to exit and restart the interpreter in order for those changes to take effect.
Here's how we can run
lab01.py
interactively:python3 -i lab01.py
-
-m doctest
: Runs doctests in a particular file. Doctests are surrounded by triple quotes ("""
) within functions.Each test in the file consists of
>>>
followed by some Python code and the expected output (though the>>>
are not seen in the output of the doctest command).def add(x, y):
"""
>>> add(5, 5)
10
>>> add(2,2)
4
"""
return x + yTo run doctests for
lab01.py
, we can run:python3 -m doctest lab01.py
Pair programming
You can use this lab as a way to try out pair programming. Check out the pair programming page.
Topics Review
Consult this section if you need a refresher on the material for this lab. It's okay to skip directly to the questions and refer back here should you get stuck.
The topics in this section are all material you should be familiar with from your prior programming experiences. This section is just a quick overview of the Python syntax for all of these topics. If any of the topics here are unfamiliar, or you feel uncomfortable with, you may consider switching to the CS 110, Introduction to Programming class.
- Basic Math
- Functions
- Control Statements
- Error Messages
Division, Floor Div, and Modulo
Let's compare the different division-related operators in Python 3:
True Division: / (decimal division) |
Floor Division: // (integer division) |
Modulo: % (remainder) |
---|---|---|
|
|
|
Notice that Python outputs ZeroDivisionError
for certain cases. We
will go over this later in this lab under Error
Messages.
One useful technique involving the %
operator is to check whether a
number x
is divisible by another number y
:
x % y == 0
For example, in order to check if x
is an even number:
>>> x = 1234 + 5678 * 9
>>> x % 2 == 0
True
Functions
If we want to execute a series of statements over and over, we can abstract them away into a function to avoid repeating code.
For example, let's say we want to know the results of multiplying the numbers 1-3 by 3 and then adding 2 to it. Here's one way to do it:
>>> 1 * 3 + 2
5
>>> 2 * 3 + 2
8
>>> 3 * 3 + 2
11
If we wanted to do this with a larger set of numbers, that'd be a lot of repeated code! Let's write a function to capture this operation given any input number.
def foo(x):
return x * 3 + 2
This function, called foo
, takes in a single argument and will
return the result of multiplying that argument by 3 and adding 2.
Now we can call this function whenever we want this operation to be done:
>>> foo(1)
5
>>> foo(2)
8
>>> foo(1000)
3002
Applying a function to some arguments is done with a call expression.
Call Expressions
A call expression applies a function, which may or may not accept arguments. The call expression evaluates to the function's return value.
The syntax of a function call:
add ( 2 , 3 )
| | |
operator operand operand
Every call expression requires a set of parentheses delimiting its comma-separated operands.
To evaluate a function call:
- Evaluate the operator, and then the operands (from left to right).
- Apply the operator to the operands (the values of the operands).
If an operand is a nested call expression, then these two steps are applied to that inner operand first in order to evaluate the outer operand.
return
and print
Most functions that you define will contain a return
statement. The
return
statement will give the result of some computation back to the
caller of the function and exit the function. For example, the function
square
below takes in a number x
and returns its square.
def square(x):
"""
>>> square(4)
16
"""
return x * x
When Python executes a return
statement, the function terminates
immediately. If Python reaches the end of the function body without
executing a return
statement, it will automatically return None
.
In contrast, the print
function is used to display values in the
Terminal. This can lead to some confusion between print
and return
because calling a function in the Python interpreter will print out the
function's return value.
However, unlike a return
statement, when Python evaluates a print
expression, the function does not terminate immediately.
def what_prints():
print('Hello World!')
return 'Exiting this function.'
print('CS111 is awesome!')
>>> what_prints()
Hello World!
'Exiting this function.'
Notice also that
return
will preserve the quotes.
Control
Boolean Operators
Python supports three boolean operators: and
, or
, and not
:
>>> a = 4
>>> a < 2 and a > 0
False
>>> a < 2 or a > 0
True
>>> not (a > 0)
False
and
evaluates toTrue
only if both operands evaluate toTrue
. If at least one operand isFalse
, thenand
evaluates toFalse
.or
evaluates toTrue
if at least one operand evaluates toTrue
. If both operands areFalse
, thenor
evaluates toFalse
.not
evaluates toTrue
if its operand evaluates toFalse
. It evaluates toFalse
if its operand evalutes toTrue
.
What do you think the following expression evaluates to? Try it out in the Python interpreter.
>>> True and not False or not True and False
It is difficult to read complex expressions, like the one above, and understand how a program will behave. Using parentheses can make your code easier to understand. Python interprets that expression in the following way:
>>> (True and (not False)) or ((not True) and False)
This is because boolean operators, like arithmetic operators, have an order of operation:
not
has the highest priorityand
or
has the lowest priority
Truthy and Falsey Values: It turns out and
and or
work on more
than just booleans (True
, False
). Python values such as 0
, None
,
''
(the empty string), and []
(the empty list) are considered false
values. All other values are considered true values.
Short Circuiting
What do you think will happen if we type the following into Python?
1 / 0
Try it out in Python! You should see a ZeroDivisionError
. But what
about this expression?
True or 1 / 0
Consider this expression as well.
True and 0 and 1 / 0
The first one evaluates to True
and the second will evaluate to 0,
because Python's and
and or
operators short-circuit. That is,
they don't necessarily evaluate every operand.
Operator | Checks if: | Evaluates from left to right up to: | Example |
---|---|---|---|
and |
All values are true | The first false value | False and 1 / 0 evaluates to False |
or |
At least one value is true | The first true value | True or 1 / 0 evaluates to True |
Short-circuiting happens when the operator reaches an operand that
allows them to make a conclusion about the expression. For example,
and
will short-circuit as soon as it reaches the first false value
because it then knows that not all the values are true.
If and
and or
do not short-circuit, they just return the last
value; another way to remember this is that and
and or
always return
the last thing they evaluate, whether they short circuit or not. Keep in
mind that and
and or
don't always return booleans when using values
other than True
and False
.
Try doing Q1: WWPD: Veritasiness
If Statements
Conditional statements let programs execute different lines of code depending on certain conditions. Let’s review the if-elif-else syntax. The general form looks like this.
if <conditional expression>:
<suite of statements>
elif <conditional expression>:
<suite of statements>
else:
<suite of statements>
- The
else if
/elif
andelse
clauses are optional, and you can have any number ofelif
clauses, but only 1else
. - A conditional expression is an expression that evaluates to either a truthy value (True, a non-zero integer, etc.) or a falsy value (False, 0, None, "", [], etc.).
- Only the first if/elif expression that evaluates to a truthy value will execute its indented code block.
- If none of the conditional expressions evaluate to a true value, then the else suite is executed.
You can review the syntax of if
statements further in Section
1.5.4
of Composing Programs.
Tip: We sometimes see code that looks like this:
if x > 3:
return True
else:
return FalseThis can be written more concisely as
return x > 3
. If your code looks like the code above, see if you can rewrite it more clearly!
While Loops
You can review the syntax of while
loops in Section
1.5.5 of Composing Programs.
It will also be reviewed down below.
A while
loop requires an expression that evaluates to either a truthy or falsey value. As
long as that expression evaluates to truthy, the code in the while
loop's body will continue
to execute. Because of this, you must be careful not to put a condition that will never become
falsey or else the while
loop will run forever! Here's the syntax:
while <conditional expression>:
<suite of statements>
Here's an example of a while
loop that will iterate (execute the loop's body) 5 times
using a variable called i
.
i = 0
while i < 5:
print(i)
i += 1 # equivalent to `i = i + 1`
Try doing Q2: WWPD: Control
Error Messages
By now, you've probably seen a couple of error messages. They might look intimidating, but error messages are very helpful for debugging code. The following are some common types of errors:
Error Types | Descriptions |
---|---|
SyntaxError |
Contained improper syntax (e.g. missing a colon after an if statement or forgetting to close parentheses/quotes) |
IndentationError |
Contained improper indentation (e.g. inconsistent indentation of a function body) |
TypeError |
Attempted operation on incompatible types (e.g. trying to add a function and a number) or called function with the wrong number of arguments |
ZeroDivisionError |
Attempted division by zero |
Using these descriptions of error messages, you should be able to get a better idea of what went wrong with your code. If you run into error messages, try to identify the problem before asking for help. You can often Google unfamiliar error messages to see if others have made similar mistakes to help you debug.
For example:
>>> square(3, 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: square() takes 1 positional argument but 2 were given
Note:
- The last line of an error message tells us the type of the error. In
the example above, we have a
TypeError
. - The error message tells us what we did wrong -- we gave
square
2 arguments when it can only take in 1 argument. In general, the last line is the most helpful. - The second to last line of the error message tells us on which line
the error occurred. This helps us track down the error. In the
example above,
TypeError
occurred atline 1
.
Required Questions
What Would Python Display? (WWPD)
Q1: WWPD: Veritasiness
The "What Would Python Display?" (WWPD) questions are designed to test your knowledge and understanding of what the given Python code is doing. You should read the code and try to determine what would be displayed by the Python interpreter. After you've made your guess, or if you are completely stumped on what it is doing, type the code in to the interpreter and see what happens and if you were correct. If not, spend some time figuring out what was wrong.
Use the following "What Would Python Display?" questions to test your knowledge on short-circuiting:
>>> True and 13
______
>>> False or 0
______
>>> not 10
______
>>> not None
______
>>> True and 1 / 0 and False
______
>>> True or 1 / 0 or False
______
>>> True and 0
______
>>> False or 1
______
>>> 1 and 3 and 6 and 10 and 15
______
>>> -1 and 1 > 0
______
>>> 0 or False or 2 or 1 / 0
______
>>> not 0
______
>>> (1 + 1) and 1
______
>>> 1/0 or True
______
>>> (True or False) and False
______
Q2: WWPD: Control
Use the following "What Would Python Display?" questions to test your knowledge on control:
Hint: Make sure your
while
loop conditions eventually evaluate to a false value, or they'll never stop! Typing [Ctrl]+[C] will stop infinite loops in the interpreter.
>>> def func(a, b):
... if a == 4:
... return 6
... elif b % 2 == 0:
... return a // 2
... else:
... return 3 * b + 1
>>> func(10, 6)
______
>>> func(4, 6)
______
>>> func(0, 3)
______
>>> def how_big(x):
... if x > 10:
... print('huge')
... elif x > 5:
... return 'big'
... elif x > 0:
... print('small')
... else:
... print("nothing")
>>> how_big(7)
______
>>> how_big(12)
______
>>> how_big(-1)
______
>>> n = 3
>>> while n >= 0:
... n -= 1
... print(n)
______
______
______
______
>>> positive = 13
>>> while positive:
... print("positive?")
... positive -= 3
______
Coding Practice
Q3: Falling Factorial
Let's write a function falling
, which is a "falling" factorial that
takes two arguments, n
and k
, and returns the product of k
consecutive numbers, starting from n
and working downwards. When k
is 0, the function should return 1.
def falling(n, k):
"""Compute the falling factorial of n to depth k.
>>> falling(6, 3) # 6 * 5 * 4
120
>>> falling(4, 3) # 4 * 3 * 2
24
>>> falling(4, 1) # 4
4
>>> falling(4, 0)
1
"""
"*** YOUR CODE HERE ***"
Thinking about your code
Now that you have the code running, discuss the following questions with your group.
- What happens if k is larger than n?
- Does that make sense for a factorial?
- How would you change the code to fix that problem?
- n should be a positive number, how would you modify your code to ensure that it doesn't run if n is negative?
Q4: Sum Digits
Write a function that takes in a non-negative integer and sums its individual digits.
Try to do this problem without converting the integer into a string.
def sum_digits(y):
"""Sum all the digits of y.
>>> sum_digits(10) # 1 + 0 = 1
1
>>> sum_digits(4224) # 4 + 2 + 2 + 4 = 12
12
>>> sum_digits(1234567890)
45
>>> a = sum_digits(123) # make sure that you are using return rather than print
>>> a
6
"""
"*** YOUR CODE HERE ***"
Hint: Given some integer
n
,n % 10
would evaluate to the rightmost digit inn
. Doingn // 10
would 'slice off' the rightmost digit ofn
. Demo the two expressions. How can you use these two expressions to grab each digit and accumulate it to the sum?
Thinking about your code
Discuss the following with your group:
- What if you wanted to accept negative numbers? Does the function you wrote work? If not, what would you have to change?
Submit
For each lab, even if you attend lab, you will have to submit working code to get the points.
Submissions will be in Canvas.
Submit the lab01.py
file to Gradescope in the window on the assignment page.
Extra Practice
These questions are optional and will not affect your score on this assignment. However, they are great practice for future assignments, projects, and exams. Attempting these questions can be valuable in helping cement your knowledge of course concepts.
Q5: Debugging
The following is a quick resource on different debugging techniques that will be helpful for you to use in this class. You can refer to this debugging article to answer debugging questions.
Q6: WWPD: What If?
Use the following "What Would Python Display?" questions to test your knowledge:
Hint:
return
) does not cause the function to exit.
>>> def ab(c, d):
... if c > 5:
... print(c)
... elif c > 7:
... print(d)
... print('foo')
>>> ab(10, 20)
______
______
>>> def bake(cake, make):
... if cake == 0:
... cake = cake + 1
... print(cake)
... if cake == 1:
... print(make)
... else:
... return cake
... return make
>>> bake(0, 29)
______
______
______
>>> bake(1, "mashed potatoes")
______
______
Q7: Double Eights
Write a function that takes in a number and determines if the digits contain two adjacent 8s.
def double_eights(n):
"""Return true if n has two eights in a row.
>>> double_eights(8)
False
>>> double_eights(88)
True
>>> double_eights(2882)
True
>>> double_eights(880088)
True
>>> double_eights(12345)
False
>>> double_eights(80808080)
False
"""
"*** YOUR CODE HERE ***"