Introduction
This tutorial is meant as a brief introduction to the building blocks of Python and, to some extent, computer science in general. Its target audience is someone who is just beginning to learn the fundamentals of computer science, and can be used as a teaching/studying tool. Those reading this guide may wish to read it several times: perhaps every few days while they are beginning their journey into learning Python. The goal should be to know all of these topics and definitions by heart.
Content is organized into different sections that each reinforce various aspects of Python. Each section assumes knowledge of the previous, and attempts to use no knowledge from future sections.
Without further ado, let's begin!
Section 1: Data Types
A data type is a specific representation of a piece of data. Consider some pieces of data about yourself: you have a name, an age, and a home address. Your name is a piece of data represented by a sequence of letters. Everyone's name is really just a sequence of letters, so we can think about the data type of someone's name as being a sequence of letters. Your age is a number, so its data type is a number. Your address is a more complex data type, but generally consists of a house number, street name, city, state, and zip-code. All of those things make up the data type known as an address.
Python comes with several built-in data types that we can use. These are:
- None: a special type that represents the idea of "nothing." The only valid value of the
None
type isNone
. - Integers: whole numbers, such as
5
or -12
or432132
. Integers include negative numbers and0
. - Floats: decimal numbers, such as
1.5
or12.5256
. - Strings: a sequence of characters surrounded by quotes. The quotes themselves are not part of the string, but are just a way to tell Python "the characters between these quotes are a string, not code!" An example of a string is
"hello world"
- Booleans: a value that is either true or false. The only values that a boolean can be is either
True
orFalse
. Booleans are useful for making logical decisions about which lines of code get executed in a program. We will explore this in a later section. - Lists: a collection of items in a specific order. The items inside a list can be different types. To create a list, we write a sequence of comma-separated items surrounded by square brackets, such as
["hello world", "this is a list", 42, -25, 1.5]
. If we have a list, we can add items to it, which we will explore later. - Tuples: a collection of items in a specific order that cannot be changed. Tuples are identical to lists except that once they are created, they cannot be changed (we cannot add items to them). To create a tuple, we use parentheses instead of square brackets:
("hello world", "this is a tuple", 12, -1.25)
. - Sets: a collection of unique items that have no specific order. Unlike lists and tuples, items in a set do not have an order. You can think of a set as a bag of items. You can add items to the bag, peek inside the bag to find out what's in it, and you can even keep pulling random items out of the bag one at a time until you run out of things in the bag. But, you can't line all the items of the bag up in a specific order. To create a set, you provide a sequence of comma-separated values surrounded by curly braces:
{"hello world", "this is a set", 42, True}
. The items of a set must be immutable, which we will discuss later. - Dictionaries: a collection of (key, value) pairs. A dictionary maps keys to their corresponding values. You can imagine a dictionary like a table with two columns: on the left is a key, and on the right is the value for that key. To create a dictionary, you provide a sequence of comma-separated key value pairs (where the key and value are separated by a colon), surrounded by curly braces. For example,
{"luke": 22, "phil": 36, "jacob": 12}
is a dictionary. The keys of a dictionary must be immutable, which we will discuss right now!
Section 2: Mutable vs. Immutable
In Python, data types are either mutable or immutable. In English, the word "mutable" means "can change." Likewise, "immutable" means "cannot change." Python types that are immutable are:
- Integers
- Floats
- Booleans
- Strings
- Tuples
Python types that are mutable are:
- Lists
- Sets
- Dictionaries
This should make sense, since we can add elements to lists, sets, and dictionaries. Remember that only immutable things can be elements of sets or keys in dictionaries!
Section 3: Variables
It is often useful to create some piece of data and be able to access it again later. To do this, we can create a variable. If we want to define a variable my_string
to be "hello world"
we would write
my_string = "hello world"
Now, every time we reference my_string
, we are really referencing the string "hello world"
. For example, if we then went on to create a list [my_string, "I used a variable!"]
, it would really be the list ["hello world", "I used a variable!"]
.
It's important to note that the value a variable refers to is looked up when we first use it. For example, consider the code
my_string = "hello world"
my_list = [my_string , "I used a variable!"]
my_string = "goodbye world"
The value of my_list is still ["hello world", "I used a variable!"]
, and NOT ["goodbye world", "I used a variable!"]
. This is because my_string
is looked up on the second line, when the list is created.
However, remember that some objects are mutable. Consider the following code:
my_first_list = ["hello world"]
my_second_list = [my_first_list, "how are you?"]
my_first_list[0] = "goodbye world"
(the third line will be discussed in the next section; it is just changing the first element of my_first_list). What is the value of my_second_list
now? It will actually be [["goodbye world"], "how are you?"]
. This is because my_first_list
is a list which is being changed. When my_first_list
is evaluated on the second line, it finds a list, and so my_second_list
just remembers "okay, my first element is a list. Whatever the current value of that list is will be my first element." Line 3 changes that list, so the change is reflected in my_second_list
.
Let's quickly compare this code to
my_first_list = ["hello world"]
my_second_list = [my_first_list, "how are you?"]
my_first_list = ["goodbye world"]
What's the value of my_second_list
in this case? It's actually [["hello world"], "how are you?"]
! This is because the third line is changing the actual list which my_first_list
refers to, not the original list. Before you move on, you should make sure this makes complete sense :)
Section 4: Indexing
A lot of the data types we've seen are collections of items. We should really have a way to access specific items within a collection. We call retrieving a specific item from a collection "indexing into" the collection. To "index into" a collection, we used square brackets surrounding the index we which to get. For example, given the code
my_list = ["hello world", "goodbye world"]
x = my_list[0]
x
will have the value "hello world"
. In Python, indices (plural of index) start at 0
, meaning the first item of a list has an index of 0
. If the list has n
items, the last item has index n - 1
.
We can also "index into" a string or tuple. For example, given the code
my_tuple = ("hello world", "goodbye world")
x = my_tuple[1]
y = x[0]
x
will have a value of "goodbye world"
since that is the second (index 1
) item of my_tuple
. y
will have a value of "g"
since that is the character at index 0
of the x
(which, remember, is the string "goodbye world"
).
We can also "index into" a dictionary to get the value for a specific key. When we "indexed into" a list, tuple, or string, the index we used was an integer that represented the position of the item we wanted. For dictionaries, our index will be the key for the value we want. For example, given the code
my_dict = {"luke": 22, "john": 12}
x = my_dict["luke"]
x
will be 22
, since that is the value associated with the key "luke"
.
We can also use indexing to define a value inside a list of dictionary. For example, the code
my_list = ["hello world", "goodbye world"]
my_list[1] = "how are you?"
will result in my_list
having a value of ["hello world", "how are you?"]
.
Likewise, we can change the value for a key in (or add an entirely new key/value pair to) a dictionary! The code
my_dict = {"luke": 22, "john": 12}
my_dict["luke"] = 50
my_dict["jacob"]: 123
will result in my_dict
being {"luke": 50, "john": 12, "jacob": 123}
.
A final note about indexing: if you try to retrieve an element that does not exist (either by providing a list/string an index that's higher than its final item's index, or by providing a dictionary a key it does not have), Python will not know what to do and stop executing code.
Section 5: Expressions
In Python, it is useful to be able to write out entire expressions that evaluate to some final value. Two common types of expressions are:
- Arithmetic expressions: expressions that perform some kind of math and result in some type of number (either an integer or decimal). Python supports the standard mathematical operations: addition (
+
), subtraction (-
), multiplication (*
), division (/
), and exponentiation (**
). If we writex = 1 + 2
,x
will have a value of3
. If we use multiple operations at the same time, Python will follow PEMDAS.x = 1 + 2 * 3
will result inx
having a value of7
, since2 * 3
is performed first. Just like in regular math, we can use parentheses to group together operations that should be performed first. Another common operation is the modulus (%
) operation, which returns the remainder of division. For example, if we writex = 7 % 5
,x
would have a value of2
. - Logical expressions: expressions that result in a boolean: either
True
orFalse
. Some common logical operations are less than (<
), greater than (>
), less than or equal to (<=
), greater than or equal to (>=
), equal to (==
), and not equal to (!=
). If we writex = 2 > 1
,x
would have the valueTrue
, since2
is greater than1
.
We can also create complex logical expressions using and
and or
. If we write x = 2 > 1 and 10 < 20
, x
will have a value of False
. The logical expression a and b
evaluates to True
if and only if both a
and b
are themselves True
.
However, if we wrote x = 2 > 1 or 10 < 20
, x
would be True
. This is because the logical expression a or b
evaluates to True
if one or both of a
or b
are True
We can combine and
and or
expressions to create arbitrarily complex logical expressions. For example, we could write (a and (b or c)) or (d or (e and f))
. Try to figure out when this evaluates to True
!
Lastly, technically, anything that results in some kind of evaluation to a value is considered an expression. For example, the line "hello world" + " how are you?"
is an expression that results in a value of "hello world how are you?"
Indeed, strings, like numbers, can be "added" to produce a new string!
Section 6: Functions and Methods
We often want to write re-usable code. That is, pieces of code that can be run multiple times without typing it out every time. To do this, we can create and use functions.
A function is a block of code that does something and potentially returns a value. If we want to call a function, we write the function name followed by parentheses with arguments, if there are any. A useful function that Python provides is the print
function, which takes one argument: the thing to print. What print does it print a text representation of whatever was given to the function to the Python window. For example, print("hello world")
will result in the text hello world
being printed to the screen.
We can also define our own functions. Suppose we want to write a function called print_hello
that only does one thing: it prints "hello world"
to the console. We can do that by writing
def print_hello():
print("hello world")
Now, if we want to call our function, we write can write print_hello()
.
The first line of our little program is def print_hello():
. Let's analyze the parts of this line. First, the keyword def
is used to tell Python "I am defining a function." Next, we wrote print_hello
, which tells Python "the name of the function I am defining is print_hello." Then, we wrote ()
. Every function definition has parentheses after the name, which is where we would put the inputs to the function, if it has any. Lastly, we put a :
to tell Python "this is the start of the function code."
Every indented line that follows the colon is part of the function. I will repeat this: every indented line that follows the colon is part of the function. If there is a line that is not indented, Python will think the function has no more lines. This is very important to remember when writing Python code!
Note that our function does not take in any arguments. Let's write a new function add_numbers
which takes two numbers and prints their sum.
def add_numbers(a, b):
print(a + b)
We would call this by writing, for instance, add_numbers(1, 2)
. In our function definition, we wrote (a, b)
, which tells Python "every instance of the variables a
and b
in this function refer to the values passed in by whoever called this function." We don't know what values are going to be passed in, so we just use the variables in our function's code.
So far, we've only see functions that "do something." We haven't seen any functions that return anything. Let's change our add_numbers
code to not print the sum, but instead return it.
def add_numbers(a, b):
return a + b
Now, the code
x = add_numbers(2, 3)
print(x)
would print 5 to the console. Whenever we call a function, we can think of the function's call-site (add_numbers(2, 3)
here) as being replaced with whatever the function returns. So, after the first line, x
has the value of 5
, since add_numbers(2, 3)
returned 5
. Before moving on, make sure this makes complete sense.
Now, let's discuss methods. Methods are a fancy name for functions that are operating on something. They behave exactly like a function: you can call them using parentheses and pass in arguments inside the parentheses. They "do something" and potentially return a value. The only difference between methods and functions is that methods are "operating" on something.
Let's look at an example of a method. Consider the code
my_list = ["hello world", "goodbye world"]
my_list.append("how are you?")
After this code, my_list
has a value of ["hello world", "goodbye world", "how are you?"]
. This is because append
is a method that operates on a list. What append
does is add the argument passed in to the end of the list that it is operating on. So, the list that the variable my_list
is referring to does get get replaced, but the list itself changes. Again, this is what we mean when we say lists are mutable.
As you can see in this example, to call a method, you put a .
after the thing you are calling the method on, and then call the method like a regular function.
Section 7: If/Else Statements
Often, we want to execute some code only under certain circumstances. To do this, we can use an if/else
statement. Consider the following code:
if x > 100:
print("x is big!")
else:
print("x is small!")
What this code is doing is making a choice based on the logical expression x > 100
. If it's True
, it will take the path that prints "x is big!"
. Otherwise, it will follow the else
branch and print "x is small!"
Note that just like in our functions, the code "inside" of the if/else
branches have to be indented. This is how we tell Python "these lines are part of the if/else branch!" An important thing to note is that one and only one branch is ever taken in an if/else
statement.
We can also perform an arbitrary number of checks. Consider the code
if x > 1000:
print("x is HUGE!")
elif x > 500:
print("x is very big!")
elif x > 100:
print("x is kind of big!")
else:
print("x is small!")
This code will first determine if x > 1000
. If it is, it will take the "x is HUGE!"
branch. The rest of the if/else
statement is ignored, since one branch was taken.
However, suppose x > 1000
evaluates to False
. The next thing this code does is check if x > 500
. elif
is short for "else if," which means "everything above me was False
, so if my logical expression is True
..." This process will repeat until all branches are checked and one evaluates to True
. If nothing is True
, the else
branch will be taken.
Section 8: While Loops
Another way to re-use code is to create a while loop. A while loop is a block of code that gets repeatedly executed until some logical expression is False. Consider the code
x = 0
while x < 10:
print(x)
x = x + 1
This will print every number from 0
to 9
. While loops evaluate the logical expression at the start of each iteration. The code inside the while loop is executed if any only if the logical expression for that iteration is True
.
We can easily create an infinite while loop! Consider the code
while True:
print("oops")
This will print "oops"
over and over forever, since the condition the while
loop checks never evaluates to False
.
Note that just like functions and if/else branches, every line included in the while loop's code must be indented.
Section 9: For Loops
Often we want to iterate over a collection of some sort. For example, maybe we want to do something to every item in a list. We can write code like
my_list = ["hello", "goodbye", "world"]
for word in my_list:
print(word)
This will print every item in my_list
. Note that we can replace word with anything we want. for xxxx in yyyy
tells Python "iterate over yyyy
. In every iteration, make a variable called xxxx
that I can use to get the item for the current iteration."
We can also iterate over tuples, sets, and strings. Tuples work exactly like the above example. Sets work similarly: when we iterate over a set, each item is included once, but the items are iterated in any order. Lists and tuples are iterated over in the order of the items. Since sets do not have an order, we don't know when we're going to get a specific element.
When we iterate over a string, we actually iterate over its characters. For example,
for char in "hello world":
print(char)
will print "h"
then "e"
then "l"
and so on.
Lastly, we can iterate over dictionaries too! Like sets, we don't know what order we will get key/value pairs in. But we can do
my_dict = {"luke": 22, "john": 12}
for name, age in my_dict:
print(name + " is " + age + "years old")
This will print both "luke is 22 years old"
and "john is 12 years old"
in some undefined order.
Conclusion
Thus marks the end of the basics of Python. There is of course much more to explore, and I hope whoever is reading this is enjoying their journey through computer science so far! Stay tuned for more lessons, and keep reading, studying, and practicing!!