Lecture 2: Variables, Types, and Expressions¶
Welcome back to COMP116! Last time we went over the course logistics, some histories, and then started writing some code. Do you still remember what we did? In this lecture, we will go over the basic building blocks of programming. By the end of this lecture, you will be able to
- define variables that hold data
- identify the type of the variables and know the common data types used in programming
- create expressions with variables that evaluate to some value
And you will be equipped to start working on Task 1 of HW1, so just a quick reminder to start early!
From Last Lecture: print("Hello!")¶
Last time, we wrote a line of code that says print("Hello!") which prints the string Hello! to the output. This is a statement and we call it a print statement because it prints something to the screen.
There is a data type involved, string. You can think of it as text, or a group of characters. What are some other data types that you commonly see or use in the real life?
Numbers in Python¶
Yes, one of the most common data types is numeric data, numbers. There are different kinds of numeric data. Mathematically, you might know of integers, fractions, rational numbers, irrational numbers, complex numbers, and higher-dimensional numeric data like vectors, matrices, tensors... The list goes on.
Can computers represent numbers? Yes. In what format? Here's some intuition from the first lecture. If you recall the punch cards that were used both for jacquard looms and for early computers, a hole represents something is going to be there (e.g., the needle is going to be picked up), and the absence of a hole represents the opposite (i.e., the needle is not goint to be selected). That is the binary logic that both machines have in common.
Binary¶
What is binary? We usually count in 0, 1, 2, 3, through 9, and then we increase the number of digits from 1 to 2, and arrive at number 10. So we usually use base-10 numbers, which means that we increment the left digit by 1 after counting 10 increments in its immediate right digit. Binary, or base-2 numbers, by its name, means that we increment the left digit by 1 after counting 2 increments in its immediate right digit.
Example looks like this:
- 0
- 1
- 10
- 11
- 100
- 101
- 110
- 111
- ...
And that is how computers keep track of numbers or numeric data. We call each 0/1 slot a bit (binary digit), eight of them is a byte, 1,000 bytes is a kilobyte, 1,000 kilobytes is a megabyte, ... so on and so forth.
Numeric Data in Binary¶
Out of these single-number types (integers, fractions, rational numbers, irrational numbers, complex numbers), which ones can be precisely represented using a byte? Well, in fact, none, or a limited subset, can be represented precisely using a byte.
Computers can only work with finite amount of bytes for representing a number and for any number that has infinitely many digits, it is impossible to be represented with any amount of bytes. Computers also have limited amount of memory, and therefore there's always the precision and memory tradeoff. But regardless, we can represent a wide range of numbers with limited memory. In this class, we will not go any further into lower level details and just stay on the surface of saying, computers run with 0s and 1s behind the scene, but in programming languages we still use the base-10 conventions that we humans are most familiar with these days.
# our course number
116
116
# what's its type?
type(116)
int
Let's peruse what just happened a bit. You see the green colored text starting with a pound sign #, and that is a line of comment. Adding comments to your code to explain what this is doing, or why you did this, can often be helpful for people who will read this code later (yes, including yourself, so you are doing yourself a favor, and any others in the future who might edit this code, or in workplace, your coworkers who collaborate with you on this code).
In addition, we can use the function type on a number to know its type. In the few lines of code we have seen up til now, which of the following is the same "species" as type? ("Hello!", print, 116)
Similarly, we can see what other types of numbers we can represent in Python.
1.27
1.27
type(1.27)
float
# the speed of light (m/s)
3e8
300000000.0
type(3e8)
float
Python recognizes scientific notation, and defaults to the float type. But when you type an integer, it is "smart" enough to recognize that it's an integer and give it the int type; anything else with a floating point, a decimal point, is given the float type.
But int and float have a second use, kind of like an abuse of notation:
# What do you expect the output to be?
int(1.27)
# What about this?
float(116)
# And this?
int(3e8)
Now they are the same species of print and type. They are called functions, and we will talk about them later, but the intuition here is that it's like the f in f(x) that takes some inputs and products an output, and the inputs are surrounded by a pair of parentheses. So indeed, what int and float do here is essentially that they take an input and try to convert that input to the type its name correspond to.
If we nest the two operations with type, logically we would expect the following output:
type(int(1.27))
int
type(float(116))
float
Time to Break the Code... and Poll!¶
Let's take this type(float(116)) and try to break it! Change anywhere you want in this expression, put your answer on PollEverywhere, and discuss with your neighbors.
type(floats(116))
type(float(116)
type(float("116"))
type(float("comp116"))
Great job! Intuitively, float expects a number, and it can be a string, as long as all the characters are digits. And then there's the usual thing of typing the wrong function names, not closing the parentheses, etc.
Arithmetics in Python¶
Now that we have a grasp of number types, we can actually, do computation with them using arithmetic operators that we use normally. For example, additions, subtractions, multiplications, divisions, integer divisions, power, logarithms, ...
An arithmetic operator is a symbol that represents an arithmetic computation. For example, the plus sign, +, performs addition. The values involved in the arithmetic operation are called operands.
# adding two transcendental numbers (approximate)
3.14159 + 2.71828
5.85987
# subtracting the golden ratio from 1
1 - 0.618
0.382
# multiplying...
4 * 3
12
# division
1 / 3
0.3333333333333333
# integer division
10 // 3
# what do you expect 1 // 3 to be?
# Uncomment the line with command/ctrl + / or removing #
# 1 // 3
3
# and also modulo
10 % 3
# 1 % 3 should now be the same
# 1 % 3
1
# power, 2 to the power of 10 is...
2 ** 10
1024
You might ask, what about ^? Sometimes we see 2^10 as representing taking 2 to the power of 10. We can just try it out and see for ourselves.
2^10
8
A bit cryptic, isn't it? Well, as we talked about earlier, computers represent numbers as binary data, so there exist binary operators as well. ^ is one of them and called the exclusive or. If you are interested in how bitwise binary operators work, you can read about them here but we won't cover it in this course.
To summarize, we just saw the following symbols that are arithmetic operators in Python: +, -, *, /, //, %, **. So you can basically construct mathematical expressions that you would put into a calculator using Python and the computation can be done just like how a calculator calculates things.
Built-in Python Functions for Math¶
In addition to these symbols, there are Python functions that also do mathematical operations.
abs(-10)
10
round(3.14)
3
round(2.72)
3
Python also provides a math library that contains functions of some more commonly used operations. To use libraries in Python, we need to first import them into the current program.
import math # this is an import statement
Now we have access to all the functions and constants under the math library. You can find the entire list here, some common ones including:
- math.abs()
- math.exp(), math.pow()
- math.sin(), and other trigs
- math.log2(), math.log10(), math.log()
- math.sqrt()
- math.pi, math.e, math.inf
# e^(-1)
math.exp(-1)
0.36787944117144233
# the same power as the previous cell
math.pow(math.e, -1)
0.36787944117144233
math.sqrt(16)
4.0
# what do you notice?
math.sin(0.25*math.pi)
0.7071067811865475
# trigs take in radians instead of degrees
1 / math.sqrt(2)
0.7071067811865475
# and the numbers are not exact because we know tan(45deg)=1
math.tan(0.25*math.pi)
0.9999999999999999
Expressions¶
A collection of these operators/functions and operands is called an expression. In Python, expressions don't have to be a formula that evaluates to a number, but anything that evaluates to something is an expression. For now, we have seen mostly just numbers, and strings, but an expression can evaluate to other "species" also. In general, as long as some piece of code is evaluated to something that can be printed to the screen or like not scripted and understandable, we can say that it's an expression.
# the usual precedence of operators (and the parentheses being most priority) from math still applies
(1 + 2) * 3 - (1 + 2 * 3)
2
# this is also an expression, even though the output is not numeric
"Hello!" * 2
'Hello!Hello!'
# but not all of such multiplications are allowed, try the following:
"Hello!" * 1.5
Variables¶
Lastly, we talk about variables. We've seen numbers, strings, and expressions that are combinations of operators and basic numbers (and strings). These are all considered having some value, so numbers and strings are themselves, and expressions are those things they evaluate to. We can create names for these values, and these names are variables.
# a variable that is holding a float number
constant_pi = 3.14159
print(constant_pi)
type(constant_pi)
3.14159
float
As the name suggests, the value of the variable can be modified.
constant_pi = 3.14
print(constant_pi)
type(constant_pi)
3.14
float
Variables can be used to construct other expressions or other variables too.
constant_tau = 2 * constant_pi
print(constant_tau)
type(constant_tau)
6.28
float
Side Note: The way we created this variable is through the assignment operator, =. So the pattern looks like: variable_name = value | expression. This pattern is called an assignment statement because it assigns one thing to something else. In general, statements are just lines of code that do something. They are different from expressions in the sense that they are more like actions (think verbs), while expressions are like things (think nouns).
For example, the assignment statement assigning values to variables does not evaluate to anything, but it does change the value this variable holds:
constant_pi = 3.14159 # nothing prints to screen because this is an action
This variable can still be evaluated to its value and so it does print to the screen when we run the cell with the variable name.
constant_pi
3.14159
But a caveat of the Jupyte notebook cell is that it will only display the value of the last expression in this cell.
constant_pi # note how this gets "flushed away" by the second expression here
type(constant_pi)
float
So to display multiple values, we can print the ones before:
print(constant_pi)
type(constant_pi)
3.14159
float
Or just keep them all in the same line and connect them with commas.
constant_pi, type(constant_pi)
(3.14159, float)
Lastly, a side note on variable naming is that we do want to give variables descriptive names so that when we read the code, we know what kind of data is held by this variable. Read more in the style guide here.
Printing with Strings, Numbers, and Variables¶
So far we have seen that we can directly print string literals like "Hello!", "this is knitting", and also directly print variables like constant_pi. But what if we want to combine text and numbers in a single output? For instance, saying "The value of pi is 3.14159" instead of just printing the number by itself.
Python gives us several ways to do this. We'll focus on two methods that you'll use frequently in this course.
Method 1: Print with Commas¶
The simplest way is to separate items with commas inside the print() function. Python automatically adds spaces between the items.
# Example: Printing a knitting calculation
yards_per_ball = 220
balls_needed = 4
print("Yards per ball:", yards_per_ball)
print("Balls needed:", balls_needed)
print("Total yards:", yards_per_ball * balls_needed)
Yards per ball: 220 Balls needed: 4 Total yards: 880
Notice how print() automatically puts a space between "Total yards:" and the calculated value. This is the easiest method when you're just displaying simple information. So choose this way when you just want to see the values of things or just debugging.
Method 2: F-Strings (Format Strings)¶
F-strings are more powerful and flexible. You put an f before the opening quote, then you can embed variables and expressions directly inside {} curly braces.
# Same example using f-strings
yards_per_ball = 220
balls_needed = 4
print(f"Yards per ball: {yards_per_ball}")
print(f"Balls needed: {balls_needed}")
print(f"Total yards: {yards_per_ball * balls_needed}")
Yards per ball: 220 Balls needed: 4 Total yards: 880
F-strings make it easy to construct sentences with embedded calculations, and you also control the amount of whitespace between everything:
# More natural sentence structure
print(f"For {balls_needed} balls at {yards_per_ball} yards each:")
print(f"You have {yards_per_ball * balls_needed} total yards available.")
For 4 balls at 220 yards each: You have 880 total yards available.
Formatting Numbers¶
Sometimes we want to control how numbers are displayed, especially for prices or measurements. We can add format specifiers inside the {} in f-strings.
# Example: Calculating yarn cost
price_per_ball = 8.5 # for example, $8.50
balls_needed = 4
total_cost = price_per_ball * balls_needed
# Without formatting gives you just the default float display
print(f"Total cost: ${total_cost}")
# With formatting, we can always shows 2 decimal places which is standard for price display
print(f"Total cost: ${total_cost:.2f}")
Total cost: $34.0 Total cost: $34.00
The :.2f is a format specifier:
- The
:starts the format specification .2means "2 digits after the decimal point"fmeans "floating-point number"
This is especially useful for money, where we always want to show cents even if it's a whole dollar amount like $34.00 instead of $34.0.
Some other options of format specifiers include d for integers, s for strings; see the full list from the official documentation page. So choose the format string approach if you are creating output for someone else (e.g., users) to read, or you want to format the output to follow a specific convention (e.g., prices).
Try It Yourself!¶
Practice using both methods. Calculate something related to knitting (or anything you like) and print it with labels.
# Your turn! Create variables and print them with labels
# Try calculating something like:
# - How many stitches in a scarf (width × stitches per inch)
# - How long a project will take (rows ÷ rows per hour)
# - Cost per yard (price ÷ yards)
Both methods are perfectly valid Python, and you'll see both used in practice. For HW1 and most assignments in this course, f-strings will make your output look more professional.