Programming Fundamentals

Variables, Loops, and Data Structures.


Setting Up Your Programming Environment

  • Python
  • Editor

Installing Python

Currently, the latest version is Python 3.11, but if you have Python 3.9 above, you're good.


Checking If You Have Python Installed

Open up a terminal and try python --version or python3 --version. If you get a version number 3.9 or above, you already have Python installed on your system. If you get a message like command not found, you need to install Python.

To install Python, go to python.org, click on Download and follow the instructions.


VSCode

We use VSCode in this course which is a free editor available for all the major operating systems (Windows, Mac, Linux). You're free to use any editor you already have and feel comfortable with. To install VSCode, go to code.visualstudio.com/download.


Python Shell

You can run snippets of Python code by opening a new terminal window and typing python or python3 (depending on your setup). You get a message like this:

$ python3 Python 3.10.6 (v3.10.6:9c7b4bd164, Aug 1 2022, 17:13:48) [Clang 13.0.0 (clang-1300.0.29.30)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>>

This command starts a Python terminal session. You should see a Python prompt (>>>), which means your operating system has found the version of Python you just installed.


Running VSCode From the Terminal

You can run VSCode from the terminal by typing code after adding it to the path:

  • Launch VSCode.
  • Open the Command Palette (Cmd+Shift+P or Ctrl+Shift+P on Windows) and type shell command to find the Shell Command: Install 'code' command in PATH command.
  • Restart the terminal for the new Path value to take effect.
  • Now, hopefully!, you should be able to use the code command in your terminal to open VSCode.

Your First (amazing) Python Program

Open an empty folder using VSCode and create file ending in .py. Here, we're going to call it hello_world.py. Add the following code to the file:

print("Hello, World!")

Open up the terminal inside your VSCode using the Ctrl+` or Cmd+` shortcut (or click on the Terminal menu and select New Terminal).

Run the code with the following command:

python hello_world.py

You should be able to see this output:

Hello, World!

Variables & Data Types

We'll talk about different kinds of data you can work with in Python programs. We’ll also learn how to use variables to represent data in our programs.


Variables

In a programming language, a variable is like a container that holds information or data. It has a name and a value, and you can change the value as needed while your program runs.

You can add a variable in Python using variable-name = variable-value. Example:

msg = "Hi, there!" print(msg)

We’ve added a variable named msg. Every variable is connected to a value, which is the information associated with that variable. In this case the value is the "Hi, there!" text.

Run the program and you should see Hi, there! showing up in the terminal.


Variables

Let's expand the program a little more:

msg = "Hi, there!" print(msg) msg = "How's it going?" print(msg)

Now when you run the program, you should see:

Hi, there! How's it going?

You can change the value of a variable in your program at any time, and Python will always keep track of its current value.


Variable Names

  • (Rule): Variable names can contain only letters, numbers, and underscores (_). They can start with a letter or an underscore, but not with a number. For instance, you can call a variable message_1 but not 1_message.
  • (Rule): Spaces are not allowed in variable names, but underscores can be used to separate words in variable names. For example, greeting_message works but greeting message will cause errors.
  • (Rule): Avoid using Python keywords and function names as variable names. For example, do not use the word print as a variable name; Python has reserved it for a particular programmatic purpose. (We'll see lots of them in this course!). Tip: using a good editor like VSCode helps you identify such issues.
  • (Best Practice): Variable names should be short but descriptive. For example, name is better than n, student_name is better than s_n, and name_length is better than length_of_persons_name. In our hello_world.py program, I named the variable msg. It would've been better if I'd named it message!

On Naming Things

There are only two hard things in Computer Science: cache invalidation and naming things.

-- Phil Karlton


In case you're interested, read Naming Things: The Hardest Problem in Software Engineering by Tom Benner.


Think of Variables As Labels

Think of variables as labels that you can assign to values. You can also say that a variable references a certain value.


Strings

The first data type we'll discuss is a string. A string is a series of characters. Anything inside quotes is considered a string in Python, and you can use single or double quotes around your strings like this:

"This is a string." 'This is also a string.'

This flexibility allows you to use quotes and apostrophes within your strings:

'As master Yoda says, "Do or Do not. There is no try!"'

String Methods

A method is an action that Python can perform on a piece of data. Depending on the type of the data, some methods are available and some are not. Some methods come pre-baked with every installation of Python, and you also have the ability to create your own.

Here are some methods for the string data type that come with the Python standard library:

name = "steve jobs" print(name.title()) # "Steve Jobs" print(name.upper()) # "STEVE JOBS"

Every method is followed by a set of parentheses, because methods often need additional information to do their work. That information is provided inside the parentheses. The title() and upper() methods don’t need any additional information, so the parentheses are empty.


Using Variables in Strings

In some situations, you’ll want to use a variable’s value inside a string. For example, you might want to use two variables to represent a first name and a last name, respectively, and then combine those values to display someone’s full name:

first_name = "Sherlock" last_name = "Holmes" full_name = f"{first_name} {last_name}" print(full_name) # "Sherlock Holmes"

These strings are called f-strings. The f is for format, because Python formats the string by replacing the name of any variable in braces with its value.


Adding Whitespace to Strings

In programming, whitespace refers to any nonprinting characters, such as spaces, tabs, and end-of-line symbols. You can use whitespace to organize your output so it’s easier for users to read.

To add a tab to your text, use the character combination \t. To add a newline in a string, use the character combination \n:

print("Python") # Python print("\tPython") # Python print("\tPy\nthon") # Py #thon

You can also combine them together: \n\t.


Stripping Whitespace

Extra whitespace could be confusing to a program. Although we may consider hello and hello to be the same, to a Python program (or any other program in that matter) they are different. (try "hello" == "hello " in a Python shell). To get rid of the extra whitespace in a string, we can use the lstrip(), rstrip(), and strip() methods:

name = " Python " print(name.lstrip()) # will print "Python " print(name.rstrip()) # will print " Python" print(name.strip()) # will print "Python"

TRY IT YOURSELF

  1. Use a variable to represent a person’s name, and then print that person’s name in lowercase, uppercase, and title case.

  2. Find a quote from a famous person you admire. Print the quote and the name of its author. Your output should look something like the following, including the quotation marks:

    Masoud Karimi once said, “TED talks are highly overrated.”

  3. Repeat the previous exercise, but this time, represent the famous person’s name using a variable called famous_person. Then compose your message and represent it with a new variable called message. Print your message.


TRY IT YOURSELF

  1. Use a variable to represent a person’s name, and include some whitespace characters at the beginning and end of the name. Make sure you use each character combination, \t and \n, at least once.

  2. Print the name once, so the whitespace around the name is displayed. Then print the name using each of the three stripping functions, lstrip(), rstrip(), and strip().


Numbers

Numbers are used quite often in programming. Python treats numbers in several different ways, depending on how they’re being used.


Integers

Integers are numbers without a decimal point, such as 6 and 110. Python supports Integers and arithmetic operations on them:

>>> 2 + 3 5 >>> 3 * 2 6

Python treats ** as the exponent operator:

>>> 3 ** 2 9 >>> 3 ** 3 27

Floats

Python calls any number with a decimal point a float. This term is used in most programming languages, and it refers to the fact that a decimal point can appear at any position in a number. You can perform arithmetic with floats just as well:

>>> 0.2 + 0.2 0.4 >>> 2 * 0.1 0.2

Sometimes, however, you may get weird results like this:

>>> 0.2 + 0.1 0.30000000000000004

Integers vs Floats

When you divide any two numbers, you'll get a float, even if the result is a whole number, unless you use the integer division operator //:

>>> 9/3 3.0 >>> 9//3 3 >>> 9//4 2

Mixing integers with floats will also get you a float:

>>> 2 + 2.0 4.0

Underscores

You can group digits together to make them more readable using _s. Python ignores _s in numbers, they're just for readability:

print(1_000_000) # prints 1000000

None

In Python, None is a special data type that represents the absence of a value or the lack of a specific object. It is often used to indicate that a variable or a function should have a value, but no actual value has been assigned or returned.


Multiple Assignments

You can assign values to more than one variable using just a single line of code. This can help shorten your programs and make them easier to read:

first_name, last_name = "Ada", "Lovelace"

Best Practice: Do not use this method to initialize more than 3 or 4 variables at once.

Best Practice: Limit your lines in a Python program to ~80 characters long.


Constants

A constant is a variable whose value stays the same throughout the life of a program. Python doesn’t have built-in constant types (unlike some other programming languages like C or Go), but Python programmers use all capital letters to indicate a variable should be treated as a constant and never be changed:

PI = 3.14 GREATEST_MOVIE_OF_ALL_TIME = "Interstellar" INSTRUCTOR_BIRTHDAY = "09/09"

Comments

Comments allow you to add information about why you do things in a certain way in your program. They are not executed and have no impact on the way the program runs. As far as the machine is concerned, they're not there.

In Python, the hash mark (#) indicates a comment. Anything following a hash mark in your code is ignored by the Python interpreter:

# this is a comment and has no effect on the program execution print("this is not a comment") """ This is a multiline comment """

Comments

The real purpose of comments is to explain why you did something, when the reason isn’t obvious. You generally should not add comments that say what a piece of code is doing. That should be obvious from reading the code.

-- Max Alexander

Bad comment:

# integer division result = total_price // number_of_items

Good comment:

# database schema only accepts integers result = total_price // number_of_items

The Zen of Python

Experienced Python programmers will encourage you to avoid complexity and aim for simplicity whenever possible. The Python community’s philosophy is contained in “The Zen of Python” by Tim Peters.

Try import this in a Python shell to get the full list.


Lists

A list is a collection of items in a particular order. We use lists when we want to store multiple values in one variable and the order of the items also matters.

You can put anything in a Python list. That is, the items don't have to be of the same type.

Best Practice: Because a list usually contains more than one element, it’s a good idea to make the name of your list plural, such as names or movies.

In Python, square brackets ([]) indicate a list, and individual elements in the list are separated by commas:

movies = ["Interstellar", "V for Vendetta", "Django Unchained"] print(movies) # ['Interstellar', 'V for Vendetta', 'Django Unchained']

Accessing Elements in a List

You can access any element in a list by telling Python the position, or index, of the item desired. The index of the first item in a list is 0 and not 1. This is not MATLAB!

movies = ["Interstellar", "V for Vendetta", "Django Unchained"] print(movies[0]) # prints 'Interstellar' print(movies[1]) # prints 'V for Vendetta' print(movies[3]) # prints ?? Try it yourself print(movies[-1]) # prints 'Django Unchained' or LAST element of the list print(movies[-2]) # prints 'V for Vendetta' or SECOND LAST element of the list print(f"My favourite movie is {movies[0]}.")

Changing Elements in a List

The syntax for modifying an element is similar to the syntax for accessing an element in a list. To change an element, use the name of the list followed by the index of the element you want to change, and then provide the new value you want that item to have:

movies = ["Interstellar", "V for Vendetta", "Django Unchained"] print(movies[0]) # prints 'Interstellar' movies[0] = "Lord of the Rings" print(movies[0]) # prints 'Lord of the Rings'

Adding Elements to a List

You can append or insert new elements (or items) to a list. append will add a new element to the end of a list, whereas insert can add a new element at any index (or position) of a list:

movies = ["Interstellar", "V for Vendetta", "Django Unchained"] movies.append("The Matrix") print(movies) # ['Interstellar', 'V for Vendetta', 'Django Unchained', 'The Matrix'] movies.insert(1, "Kill Bill") # ['Interstellar', 'Kill Bill', 'V for Vendetta', 'Django Unchained', 'The Matrix']

Best Practice: Because insert usually requires shifting, it's slower than append. Use append if either works for you.


Removing Elements from a List

If you know the position of the item you want to remove from a list, you can use the del statement:

students = ['Alice', 'John', 'Mary'] # let's say Alice drops the course del students[0] print(students) # ['John', 'Mary']

Gotcha: When you use del, you lose the element you deleted from the list, which may not be what you wanted!


Removing Elements from a List (more gracefully)

Sometimes you’ll want to use the value of an item after you remove it from a list. For example, you want to remove a student from the class list when they drop the course, but you also want to give them their class fee back and send them a confirmation email afterward:

students = ['Alice', 'John', 'Mary'] # let's say Alice drops the course dropped_student = students.pop(0) print(students) # still prints out ['John', 'Mary'] refund_student(dropped_student) send_confirmation(dropped_student)

pop() without any arguments will remove the last element of the list.


Removing Based on Value

You can remove the first occurance of a value from a list using the remove() method:

students = ['Alice', 'John', 'Mary'] # let's say Alice drops the course students.remove('Alice') print(students) # ['John', 'Mary']

TRY IT YOURSELF

  • Try removing a non-existing element from a list. What do you think would happen?
students = ['Alice', 'John', 'Mary'] students.remove('Steve')

Sorting a List

You can sort a list using both sort() and sorted() methods.


Sorting a List

sort() changes the original list, whereas sorted() leaves it untouched (unless you want to change that).

grades = [12, 20, 35, 8, 120] grades.sort() print(grades) # [8, 12, 20, 35, 120] # now with sorted grades = [12, 20, 35, 8, 120] sorted(grades) print(grades) # [12, 20, 35, 8, 120] grades = sorted(grades) print(grades) # [8, 12, 20, 35, 120]

Sorting a List

You can use reverse=True argument in both sort() and sorted() to change the order of sorting.

grades = [12, 20, 35, 8, 120] grades.sort(reverse=True) print(grades) # [120, 35, 20, 12, 8] grades = sorted(grades, reverse=True) print(grades) # [120, 35, 20, 12, 8]

Reversing a List

You can use the reverse() method to reverse a list. Note that reverse() doesn't perform any sorting; it simply reverses the order of the list (and changes the original list!).

movies = ["Interstellar", "V for Vendetta", "Django Unchained"] movies.reverse() print(movies) # ['Django Unchained', 'V for Vendetta', 'Interstellar']

Finding the Length of a List

You can quickly find the length of a list by using the len() function.

movies = ["Interstellar", "V for Vendetta", "Django Unchained"] print(len(movies)) # 3

TRY IT YOURSELF

Think of at least five places in Calgary you’d like to visit.

  • Store the locations in a list. Make sure the list is not in alphabetical order.
  • Print your list in its original order. Don’t worry about printing the list neatly; just print it as a raw Python list.
  • Use sorted() to print your list in alphabetical order without modifying the actual list.
  • Show that your list is still in its original order by printing it.
  • Use sorted() to print your list in reverse-alphabetical order without changing the order of the original list.
  • Show that your list is still in its original order by printing it again.
  • Use reverse() to change the order of your list. Print the list to show that its order has changed.
  • Use reverse() to change the order of your list again. Print the list to show it’s back to its original order.
  • Use sort() to change your list so it’s stored in alphabetical order. Print the list to show that its order has been changed.
  • Use sort() to change your list so it’s stored in reverse-alphabetical order. Print the list to show that its order has changed.

Looping Through a List

You often need to run through all elements in a list and perform a the same task on all of them, such as sending an email to every person in a list. You can use Python’s for loop to do that.

movies = ["Interstellar", "V for Vendetta", "Django Unchained"] for movie in movies: print(movie)

The output will be:

Interstellar V for Vendetta Django Unchained

What's movie in the above code?


Looping Through a List

You can do more stuff inside a loop:

movies = ["Interstellar", "V for Vendetta", "Django Unchained"] for movie in movies: print(f"The movie name is {movie}") print(f"I like to see {movie}") print("This line will only run once and not per element in the list")

Every indented line following the line for movie in movies is considered inside the loop, and each indented line is executed once for each value in the list. Any lines of code after the for loop that are not indented are executed once without repetition.


Indentation in Python

Python uses indentation to determine how a line, or group of lines, is related to the rest of the program. In the previous examples, the lines that printed messages to individual movies were part of the for loop because they were indented. Python’s use of indentation makes code very easy to read.


Indentation Errors

Always indent the line after the for statement in a loop.

movies = ["Interstellar", "V for Vendetta", "Django Unchained"] for movie in movies: print(f"The movie name is {movie}")

Indentation Errors

Remember to indent all lines that belong to a code block (such as a for loop).

movies = ["Interstellar", "V for Vendetta", "Django Unchained"] for movie in movies: print(f"The movie name is {movie}") print(f"I like to see {movie}")

What's the output of the above code?


Indentation Errors

Unnecessary indentation:

message = "Hello!" print(message)

TRY IT YOURSELF

  • Store a few topics you'll learn in this course (SSD 101) in a list (such as Git, Programming Fundamentals, Testing, and Algorithms). Write a Python program that runs through the list and for each item prints I'm going to learn <topic> in SSD 101.

The range() function

Python’s range() function makes it easy to generate a series of numbers.

for number in range(1, 5): print(number)

Output:

1 2 3 4

The first number in range() is inclusive, but the second one exclusive! The first argument is optional. If not specified, range() will start from 0. You can also pass a third argument as a step. Try replacing range(1, 4) with range(1, 4, 2) in the above example.


Make a List of Numbers with range()

You can make a list from the results of range() using the list() function:

even_numbers = list(range(1, 6, 2)) print(even_numbers) # [1, 3, 5]

TRY IT YOURSELF

  • Write a Python program that computes the square of numbers from 0 to 20, adds them to a list, and prints the list at the end.

Hint:

squares = [] for number in range(0, 21): # ...

Fun Methods with a List of Numbers

There are a few Python methods that may come in handy when you're working on a list of numbers:

sales = [120, 45, 67, 350, 599, 1210] print(min(sales)) # 45 print(max(sales)) # 1210 print(sum(sales)) # 2391

Working with Part of a List

Instead of working with a whole list, you can also work with a specific group of items in a list, called a slice in Python.

To make a slice, you specify the index of the first and last elements you want to work with. As with the range() function, Python stops one item before the second index you specify.

movies = ["Interstellar", "V for Vendetta", "Django Unchained", "Kill Bill"] print(movies[0:2]) # ['Interstellar', 'V for Vendetta'] print(movies[2:4]) # ['Django Unchained', 'Kill Bill'] print(movies[1:]) # ['V for Vendetta', 'Django Unchained', 'Kill Bill'] print(movies[:3]) # ['Interstellar', 'V for Vendetta', 'Django Unchained'] print(movies[-2:]) # ??

Looping Through a Slice

You can loop through a slice as you would with a whole list:

movies = ["Interstellar", "V for Vendetta", "Django Unchained", "Kill Bill"] for movie in movies[:2]: print(movie)

Tuples

Tuples are like Python lists except that they cannot be changed later. We can't add, remove, or change their items. This is useful if you want to have a list whose items must not change during the program. Use () instead of [] to create a tuple.

movies = ["Interstellar", "V for Vendetta", "Django Unchained"] print(movies[0]) # Interstellar movies[0] = "Kill Bill" # this is fine movies = ("Interstellar", "V for Vendetta", "Django Unchained") print(movies[0]) # Interstellar movies[0] = "Kill Bill" # this is NOT fine!

Gotcha: Tuples are technically defined by the presence of a comma. In order to create a tuple with one element, you need to do this:

movies = ("Interstellar", )

Looping Through a Tuple

You can loop through a tuple just like you would for a list:

movies = ("Interstellar", "V for Vendetta", "Django Unchained") for movie in movies: print(movie) # slicing a tuple for movie in movies(1:): print(movie)

TRY IT YOURSELF

  • What happens if you try to reassign a new tuple to a variable that's already pointing to a tuple?
movies = ("Interstellar", "V for Vendetta", "Django Unchained") movies = ("Kill Bill",)

Dictionaries

Another complex data type in Python is the Python dictionary. A dictionary is a collection of key-value pairs. Each key is connected to a value, and you can use a key to access the value associated with that key (unlike lists where you use an index). A key's value could be any type you can create in Python, such as strings, numbers, lists, dictionaries, etc.

In Python, a dictionary is wrapped in braces ({}) with a series of key-value pairs inside them.

ssd101_subjects = { "chapter_1": "Git", "chapter_2": "Programming Fundamentals", "chapter_3": "Testing" } print(ssd101_subjects) # {'chapter_1': 'Git', 'chapter_2': 'Programming Fundamentals', 'chapter_3': 'Testing'}

You can have an unlimited number of key-value pairs in a dictionary!


Accessing Values in a Dictionary

To get the value associated with a key, give the name of the dictionary and then place the key inside a set of square brackets:

ssd101_subjects = { "chapter_1": "Git", "chapter_2": "Programming Fundamentals", "chapter_3": "Testing" } first_chapter = ssd101_subjects["chapter_1"] print(f"First, we're going to learn about {first_chapter}. Yoohoooo!!")

TRY IT YOURSELF

  • Try accessing a key that doesn't exist in a dictionary. What do you think would happen?
ssd101_subjects = { "chapter_1": "Git", "chapter_2": "Programming Fundamentals", "chapter_3": "Testing" } print(ssd101_subjects["chapter_4"])

Using get() to Access Values

Using keys in square brackets ([]) to retrieve the value you’re interested in from a dictionary might cause one potential problem: if the key you ask for doesn’t exist, you’ll get an error.

get() method to the rescue! The get() method requires a key as a first argument. As a second optional argument, you can pass the value to be returned if the key doesn’t exist. No more errors!

ssd101_subjects = { "chapter_1": "Git", "chapter_2": "Programming Fundamentals", "chapter_3": "Testing" } print(ssd101_subjects.get("chapter_4")) # prints None, but no error print(ssd101_subjects.get("chapter_4", "TBD")) # prints 'TBD'

Adding New Key-Value Pairs

Dictionaries are dynamic structures, and you can add new key-value pairs to a dictionary at any time.

ssd101_subjects = { "chapter_1": "Git", "chapter_2": "Programming Fundamentals", "chapter_3": "Testing" } ssd101_subjects["chapter_4"] = "Algorithms & Data Structure"

Good to know: In Python, dictionaries retain the order of keys in which they were defined. (this is a big deal actually!)


An Empty Dictionary

You can start with an empty dictionary and add items as you go furthur in your program. Sometimes this is necessary as you don't know in advance what you'll have in your dictionary. e.g. when you're getting information from an external source.

movie_ratings = {} movie_ratings["Interstellar"] = 10 movie_ratings["V for Vendetta"] = 9.8 movie_ratings["Lord of the Rings"] = 9.5 print(movie_ratings) # {'Interstellar': 10, 'V for Vendetta': 9.8, 'Lord of the Rings': 9.5}

Changing an Existing Value in a Dictionary

We can change the value of a key at any time using the key and a new value:

movie_ratings = {} movie_ratings["Interstellar"] = 10 print(f"IMDB rating of 'Interstellar' is {movie_ratings['Interstellar']}") movie_ratings["Interstellar"] = 9.8 print(f"IMDB rating of 'Interstellar' is now {movie_ratings['Interstellar']}")

Removing Key-Value Pairs

You can remove a key-value pair using the del statement, like you would with a Python list. del needs the name of the dictionary and the key you want to delete.

movie_ratings = {} movie_ratings["Interstellar"] = 10 movie_ratings["V for Vendetta"] = 9.8 del movie_ratings["Interstellar"] print(movie_ratings) # {'V for Vendetta': 9.8}

Looping Through a Dictionary (method 1)

You can run through all key-value pair in a dictionary using the items() method:

movie_ratings = {} movie_ratings["Interstellar"] = 10 movie_ratings["V for Vendetta"] = 9.8 movie_ratings["Lord of the Rings"] = 9.5 for movie, rating in movie_ratings.items(): print(f"Movie {movie} has the rating: {rating}.") # Movie Interstellar has the rating: 10. # Movie V for Vendetta has the rating: 9.8. # Movie Lord of the Rings has the rating: 9.5.

Looping Through a Dictionary (method 2)

You can choose to only loop through the keys in a dictionary using the keys() method:

movie_ratings = {} movie_ratings["Interstellar"] = 10 movie_ratings["V for Vendetta"] = 9.8 movie_ratings["Lord of the Rings"] = 9.5 for movie in movie_ratings.keys(): print(f"Movie {movie} exists in the dictionary.") # Movie Interstellar exists in the dictionary. # Movie V for Vendetta exists in the dictionary. # Movie Lord of the Rings exists in the dictionary.

Looping Through a Dictionary (still method 2)

Because the keys() method returns a list, you can perform an action on the list of keys in a dictionary before running through them. For example, we can sort the keys alphabetically first:

movie_ratings = {} movie_ratings["Interstellar"] = 10 movie_ratings["V for Vendetta"] = 9.8 movie_ratings["Lord of the Rings"] = 9.5 for movie in sorted(movie_ratings.keys()): print(f"Movie {movie} exists in the dictionary.") # Movie Interstellar exists in the dictionary. # Movie Lord of the Rings exists in the dictionary. # Movie V for Vendetta exists in the dictionary.

Looping Through a Dictionary (method 3)

If you are mainly interested in the values that a dictionary contains, you can use the values() method to return a sequence of values without any keys:

movie_ratings = {} movie_ratings["Interstellar"] = 10 movie_ratings["V for Vendetta"] = 9 movie_ratings["Lord of the Rings"] = 8 all_ratings = movie_ratings.values() print(f"Average rating: {sum(all_ratings) / len(all_ratings)}") # Average rating: 9.0

TRY IT YOURSELF

  • Write a Python program that stores the provinces of Canada and their capitals in a dictionary. Run through the dictionary and print out each province with its capital on a separate line.

Nesting

Sometimes you’ll want to store multiple dictionaries in a list, or a list of items as a value in a dictionary. This is called nesting. You can nest dictionaries inside a list, a list of items inside a dictionary, or even a dictionary inside another dictionary.


A List of Dictionaries

person_1 = {"name": "Alice", "major": "Software Engineering"} person_2 = {"name": "John", "major": "Electrical Engineering"} person_3 = {"name": "Mary", "major": "Mechanical Engineering"} people = [person_1, person_2, person_3] for person in people: print(f"{person['name']} is taking {person['major']} at U of C.") # Alice is taking Software Engineering at U of C. # John is taking Electrical Engineering at U of C. # Mary is taking Mechanical Engineering at U of C.

A List in a Dictionary

person_1 = {"name": "Alice", "major": "Software ", "grades": ["A", "B+", "A+"]} print(person_1["grades"]) # ['A', 'B+', 'A+'] person_1 = {"name": "Alice", "major": "Software Engineering", "grades": ["A", "B+", "A+"]} person_2 = {"name": "John", "major": "Electrical Engineering", "grades": ["A+", "B-", "B+"]} people = [person_1] people.append(person_2) for person in people: print(f"Grades for {person['name']}: {person['grades']}.") # Grades for Alice: ['A', 'B+', 'A+']. # Grades for John: ['A+', 'B-', 'B+'].

A Dictionary in a Dictionary

students = { "Alice": { "major": "Software Engineering", "grades": ["A", "B+", "A+"] }, "John": { "major": "Electrical Engineering", "grades": ["A+", "B-", "B+"] } } for student_name, student_info in students.items(): print(f"Student {student_name} is taking {student_info['major']} with grades: {student_info['grades']}.") # Student Alice is taking Software Engineering with grades: ['A', 'B+', 'A+']. # Student John is taking Electrical Engineering with grades: ['A+', 'B-', 'B+'].

TRY IT YOURSELF

  • Create a phone book in Python using a dictionary. Use names as the keys and a dictionary of information (such as phone number, address, and email) as the value for each person. That is, you should be able to look up Alice in the dictionary and get her phone number, address and email. If the person you're looking for has not yet added to the phone book, the program should show None as their info.

if Statements

Oftentimes in your program you want to decide which action to take based on a condition (e.g. is my list empty? is the temperature over a certain value? did I successfully connect to the database?). Python's if statements help you with that.


A Simple if Statement

cities = ["Toronto", "Vancouver", "Calgary", "Montreal"] for city in cities: if city == "Calgary": print(f"{city} is the best city in Canada!") else: print(f"{city} is just meh!")

What's happening here?


Conditional Test

At the heart of every if statement is an expression that can be evaluated as True or False and is called a conditional test.

If a conditional test evaluates to True, Python runs the code inside the if block, and if the test evaluates to False, Python ignores it.

In the previous example, we used the equality operator (==) which returns either True or False (don't confuse it with the assignment operator =).

Good to know: Python Shell is a great tool to play around with conditional tests.


TRY IT YOURSELF

  • Open up a Python shell and try the following statements:

    city = "Calgary" city == "Calgary"
    university = "UCalgary" university == "ucalgary" university == "Ucalgary" university == "UCalgary" university.lower() == "ucalgary"

Inequality Operator (!=)

There are times you want to check for inequality instead of equality. For example, if the destination country is not Canada, then we can't ship there:

destination_country = "Italy" if destination_country != "Canada": print("Sorry, but we don't ship there")

Mathematical Comparisons

Another group of statements that can be evaluated to True or False are mathematical comparisons.

>>> age = 19 >>> age < 21 True >>> age <= 21 True >>> age > 21 False >>> age >= 21 False

What happens if you do this?

>>> 50 > "Calgary" >>> 50 == "Calgary"

Checking Multiple Conditions

You can use the and or the or keyword to create more complex conditional tests.


and

For and, the whole conditional test will evaluate to True only if ALL the single tests evaluate to True.

>>> age = 28 >>> name = "Alice" >>> age == 28 and name == "Alice" True >>> age == 29 and name == "Alice" False >>> age == 28 and name == "alice" False >>> job = "student" >>> age == 28 and name == "Alice" and job == "student" True

You can use optional parantheses to improve readability:

(age == 28) and (name == "Alice")

or

With or, the conditional test evaluates to True when at least ONE conditional test evaluates to True; otherwise, it will evaluate to False.

>>> age = 28 >>> name = "Alice" >>> age == 28 or name == "Alice" True >>> age == 29 or name == "Alice" True >>> (age == 28) or (name == "alice") True >>> (age == 29) or (name == "alice") False

Boolean Expressions

A Boolean expression is just another name for a conditional test. A Boolean value is either True or False, just like the value of a conditional expression after it has been evaluated.

Boolean values are often used to keep track of certain conditions, such as if a user is authorized to click a button, or if getting data from the database has finished:

user_authorized = False data_gathering_finished = True

You can then use boolean values in if statements:

if user_authorized: # do something here if data_gathering_finished: # do something here

Naming Best Practice for Booleans

When it comes to naming variables of type bool, it's convention to start their name with is_:

is_user_authorized = True is_data_gathering_finished = False

if Statements (cont'd)

An if statement contains a conditional test (simple or complex) that can be evaluated to True of False.

if statements, like for loops, create a new indented block. The block can have one or more Python statements:

if age > 28: print("...") print("...") print("...")

if-else Statements

Sometimes, you want to take one or a series of actions when a conditions passes, and another set of actions when it doesn't. In this case, you can use an if-else statement:

age = 17 if age >= 18: print("You can get a driver's license") else: print("You can't get a driver's license")

The if-elif-else Stetements

Sometimes, you need to test more than two possible situations. In this case you can use the Python if-elif-else syntax. Python executes only one block in an if-elif-else chain. Once a test condition passes, Python executes the block following that condition, and skips the rest:

age = 28 if age < 4: print("You get a kid discount") elif age < 60: print("You get a normal adult fair") else: print("You get a senior discount")

Multiple elif Blocks

You can have as many elif blocks as you want:

age = 45 if age < 4: print("You get a kid discount") elif age < 39: print("You get a normal adult fair") elif age < 65: print("You get an adult experiencing midlife crisis discount") else: print("You get a senior discount")

Can you omit the else statement at the end?


Multiple if Statements

Sometimes, you want to check all the possible situations where more than one can be evaluated to True instead of just one. In this case, the if-elif-else statement doesn't help. But you can have multiple if statements one after another. Python checks every single one of them even if a previous if statement met the condition:

tax_credits = ["charity", "work-from-home", "self-employed"] if "charity" in tax_credits: print("You get a Charitable Donation Tax Credit.") if "work-from-home" in tax_credits: print("You get a Work from Home Tax Credit.") if "self-employed" in tax_credits: print("You get Self-employment Expenses Tax Credit.")

What happens if you change the last 2 if with elif?


if and Lists

We'll check a few ways you can use if statements where the conditional test is related to a Python list.


Looking for a Value in a List

You can use the in keyword to see if a particular value is in a list. In case the value is indeed in the list, the conditional test will evaluate to True, otherwise, it will be False.

cities = ["Toronto", "Vancouver", "Calgary", "Montreal"] if "Calgary" in cities: print("Now that's a good list!") else: print("This list stinks!")

You can use not in to check if a value is not in a list:

if "Calgary" not in cities:

Check if a List is Empty

Python evaluates an empty list to False and a list with an item or more to True.

cities_to_visit = [] if cities_to_visit: for city in cities_to_visit: print(f"Let's go visit {city}!") else: print("The list of cities to visits is empty. Let's staty home and eat pizza instead.")

Best Practices

Python evaluates certain values as False when in a boolean context. A quick "rule of thumb" is that all "empty" values are considered False: 0, None, [], {}, ''.

Use the "implicit" False or True if possible. They're easier to read and in most cases, they're also faster!

my_list = [] # good if not my_list: print("list is empty") # bad if len(my_list) == 0: print("list is empty") if my_list == []: print("list is empty")

Best Practices

Although None evaluates to False, distinguish between the two as they're different in nature. None means an absence of a value, whereas False is a value:

a = None b = [] # in order to check for None, use "is None" or "is not None" if a is None: print("...") # in order to check for empty/non-empty, use the implicit False value if b: print("...")

This will make more sense when we talk about Python functions.


Functions

Functions are named blocks of code to do one specific job. When you want to perform a particular task that you've defined in a function, you call the function responsible for it. If you need to perform that task multiple times throughout your program, you don’t need to type all the code for the same task again and again; you just call the function dedicated to handling that task, and the call tells Python to run the code inside the function. You’ll see that using functions makes your programs easier to write, read, test, and fix.

You're also able to pass information to functions to change their behaviour. e.g. a function that adds a new record in a database table may get the record from the code calling it (the caller).


Defining a Function

# defining a function def say_cheese(): """Displays 'Cheese!'""" print("Cheese!") # calling a function say_cheese()

The def keyword tells Python that you're defining a function. This is what we call the function definition which tells Python:

  1. the name of the function
  2. what kind of information the function needs to do its job (the () holds that information)

All the indented lines that follow def say_cheese(): make up the body of the function.

The first line in the body of the function is called a docstring which is the function documentation. Python can use this docstring to generate documentation for the function (try say_cheese.__doc__).


TRY IT YOURSELF

Explain the following:

  • Function definition
  • Function body
  • Docstring
  • Function call
  • Function caller

Passing Info to a Function

Currently, the say_cheese() function only does one thing: saying 'Cheese!'. It's more common to have a function that does more than saying the same thing over and over. For instance, let's say we want a function that can say ANYTHING we want it to say. How can we do that?

def say_anything(thing): print(thing)

Here, we add thing to the function definition, which is a placeholder (or a variable) that can accept any value from the caller. We can now call the function and pass additional information:

say_anything("Ice cream so good.") # Ice cream so good.

Functions can accept more that one parameter.


Arguments vs Parameters

In the previous example, the variable thing in the definition of say_anything() is an example of a parameter, a piece of info that the function needs to perform it's job.

The value Ice cream so good. in say_anything("Ice cream so good.") is an example of an argument, a piece of info that's passed from a caller to a function.

Poeple use these terms interchangeably!


TRY IT YOURSELF

  • Write a function that accepts a social network name as a parameter named, social_network_name. The function should print that using the social network is a waste of time. Example: Using TikTok is a waste of time. Call the function 5-6 times with different social network names.

Passing Arguments

There are different ways to pass an argument to a function, which we'll be exploring next.


Positional Arguments

When you call a function, Python must match each argument in the function call with a parameter in the function definition. The simplest way to do this is based on the order of the arguments provided. Values matched up this way are called positional arguments:

def add_user(first_name, last_name): print(f"Adding {first_name} {last_name}") add_user("Jiminy", "Cricket") # Adding Jiminy Cricket add_user("Cricket", "Jiminy") # Adding Cricket Jiminy

Order matters in positional arguments.


Keyword Arguments

A keyword argument is a name-value pair that you pass to a function. You directly associate the name and the value within the argument, so when you pass the argument to the function, there's no confusion (you won't end up with Cricket Jiminy). In other words, you don't need to worry about correctly ordering the arguments in a function call this way:

def add_user(first_name, last_name): print(f"Adding {first_name} {last_name}") add_user(last_name="Cricket", first_name="Jiminy") # Adding Jiminy Cricket

Order doesn't matter here anymore. Just make sure to use the exact names of the parameters in the function definition.


Default Values

You can define a default value for each parameter in a function definition. If an argument for a particular parameter is provided in the function call, Python will use the argument value, otherwise, it will use the parameter's default value.

def add_user(first_name, last_name="Smith"): print(f"Adding {first_name} {last_name}.") add_user("John") # Adding John Smith. add_user("John", "Green") # Adding John Green.

What happens if we define a default value for the first parameter?

def add_user(first_name="John", last_name):

Default Values Gotcha

When you use default values, any parameter with a default value needs to be listed after all the parameters that don't have default values. This allows Python to continue interpreting positional arguments correctly.

Put yourself in Python's shoes. What would you do in this situation?

def add_user(first_name="John", last_name): print(f"Adding {first_name} {last_name}.") add_user("Alice") # Ambiguous!

Optional Arguments

We can use default values to make an argument optional.

def add_user(first_name, last_name, middle_name=''): if middle_name: print(f"Added {first_name} {middle_name} {last_name}.") else: print(f"Added {first_name} {last_name}.") add_user("Chandler", "Bing") add_user("Chandler", "Bing", "Muriel")

Argument Errors

When you provide fewer or more arguments than a function needs, Python will yell at you.

Try the following:

def add_user(first_name, last_name): print(f"Adding {first_name} {last_name}.") add_user() add_user("Chandler") add_user("Chandler", "Muriel", "Bing")

Passing Arbitrary Number of Arguments

Sometimes, you're not clear on how many arguments will be sent to your function. Python allows a function to accept an arbitrary number of arguments from the caller.

def get_min(*numbers): return min(numbers) min_number = get_min(-10, -12) print(min_number) # -12 min_number = get_min(1, 10, -20, 56) print(min_number) # -20

What is the type of numbers? Hint: use print(type(numbers)) inside the function.


Mixing Positional & Arbitrary Arguments

You can mix different types of arguments in Python. For instance:

def student_card(name, *grades): print(f"Student {name} has gotten these grades so far: {grades}") student_card("John Nash", "A", "A+", "B", "C+")

Arbitrary Keyword Arguments

A function can also accept an arbitrary number of keyword arguments. Remember that keyword arguments are key-value pairs used in the caller when calling a function.

def create_profile(first, last, **user_info): user_info["first"] = first user_info["last"] = last return user_info user_profile = create_profile("Daenerys", "Targaryen", title_1="Mother of Dragons", title_2="Breaker of Chains") print(user_profile)

What's the type of user_info?


TRY IT YOURSELF

What happens when you run the following code?

def create_profile(first, last, **user_info): user_info["first"] = first user_info["last"] = last return user_info user_profile = create_profile("Daenerys", "Targaryen", "Mother of Dragons", "Breaker of Chains") print(user_profile)

Return Values

A function can take a value from inside a function and return it to the caller. Return values allow you to move much of your program’s grunt work into functions, which can simplify the body of your program.

Job Interview Worthy: In some languages like SQL, the difference between a function and a similar construct (like procedure) is that a function always returns a value.


Return a Simple Value

Let's change our add_user() function to return a value instead of printing to the output:

def add_user(first_name, last_name): """Returns a message about adding the user""" return f"Added {first_name} {last_name}." message = add_user("Chandler", "Bing") print(message) # Added Chandler Bing.

Return a Complex Value

You can also return a complex value, like a list, dictionary, set, or tuple.

def create_person(first_name, last_name, middle_name=""): """Returns a dictionary of data related to the person""" person = {} person["first_name"] = first_name person["last_name"] = last_name if middle_name: person["middle_name"] = middle_name return person person = create_person("Ross", "Geller") print(person) # {'first_name': 'Ross', 'last_name': 'Geller'}

Return Multiple Values

A function can return more than one values. To return return more than one values, separate them using ,s in the return statement.

To store the multiple return values, use the multiple assignments technique we saw earlier.

def get_area_and_perimeter(length, width): """Return area and perimeter of a rectangle""" area = length * width perimeter = 2 * (length + width) return area, perimeter area, perimeter = get_area_and_perimeter(3, 7) print(f"Area: {area} and Perimeter: {perimeter}")

Best Practice

Don't use more than two return values as that would make the code hard to read. In case you want to return more than two values, use a complex type, such as a dictionary or list.


Modifying Arguments in a Function

A function can modify the arguments it receives from the caller by changing their values. However, whether or not that change gets reflected to outside of the function, depends on a very important concept in Python: Mutability!

If a type is mutable, then any change happening inside a function will get reflect to the outside too. If a type is immutable, the change only stays inside the function.


Mutable vs Immutable Types

Immutable Types:

  • Numbers
  • Strings
  • Booleans
  • Tuples

Mutable Types:

  • Lists
  • Sets
  • Dictionaries

TRY IT YOURSELF:

Try the following programs and explain the output:

def change_me(param): param[1] = 5 param.pop() my_list = [1, 2, 3] print(my_list) change_me(my_list) print(my_list) my_list = [1, 2, 3] change_me(my_list[:]) print(my_list)

TRY IT YOURSELF:

Try the following programs and explain the output:

def change_me(param): param[1] = 5 my_list = (1, 2, 3) print(my_list) change_me(my_list) print(my_list)

TRY IT YOURSELF:

Try the following programs and explain the output:

def change_me(param): param = 5 number = 10 print(number) change_me(number) print(number)

Storing Functions in Modules

You can store your functions in a separate file called a module and then import that module into your main program.

This helps mainly in two ways:

  • Cleaner programs
  • Reusability

An import statement tells Python to make the code in a module available in the currently running program file.


Creating a Module

A module is just a Python file. Simply create a file with the .py extension and define a number of functions there. For example:

In math_utils.py:

def average(*numbers): """Returns the integer average of an arbitrary number of numbers""" total = sum(numbers) length = len(numbers) return total // length

Importing a Module (method 1)

You can import (copy) an entire module using import <filename>. For example, to import the module we just created, we can use import math_utils in another Python file to import the whole module (all its functions):

import math_utils print(math_utils.average(1, 3, 4, 5))

Since we're importing the entire module, we need to specify which function we'd like to use by using the module name, followed by a ., followed by the function name.


Importing a Module (method 2)

We can also import specific functions from a module with the following syntax:

from math_utils import average print(average(1, 3, 4, 5))

Since we imported a specific function, we don't need to use the . syntax anymore.


Importing a Module (method 2) Gotcha

Importing specific functions may cause conflicts if the main program also has functions with the same names.

Try this:

from math_utils import average def average(): print("hello world!") print(average(1, 3, 4, 5))

Use as to Avoid Conflicts

In case you have conflicts with functions from another module, you can either use the first method of importing, or the second method with the as keyword to give the functions an alias (different name) to avoid conflicts.

from math_utils import average as avg def average(): print("hello world!") print(avg(1, 3, 4, 5))

Importing a Module (method 3)

You can also import ALL the functions in a module using the following syntax:

from math_utils import * print(average(1, 3, 4, 5))

Best Practice: it's best not to use this approach when you’re working with larger modules that you didn't write: if the module has a function name that matches an existing name in your project, you can get unexpected results.


User Inputs

There are times when you want to get some data from the end user of your application, such as their username or password. Python provides the input() function to help with such situations.


input() Function

The input() function pauses your program and waits for the user to enter some text. Once Python receives the user's input, it can assign it to a variable to make it convenient for you to work with:

color = input("What's your favourite colour?") print(f"Your favourite color is {color}.")

The input() function takes only one argument which is the prompt you want to display to the user. It waits until the user presses ENTER.


TRY IT YOURSELF

How can you use a multiline prompt like the following for the input() function?

Who would cross the Bridge of Death must answer me these questions three, ere the other side he see. What... is your name?

Type of Inputs

Whenever we use the input() function in Python, what we receive from the user is always a string, regardless of what the user provides. In case we need to get a number from the user, such as their age, we need to make sure to cast the value into a number before using it.

Try the following:

age = input("How old are you?") if age < 13: print("You must be at least 13 to use the application")

Casting Inputs

The previous code snippet will generate an error as you can't compare a number (13) to a string (age) in Python. In order to fix it:

age = input("How old are you?") age = int(age) if age < 13: print("You must be at least 13 to use the application")

while Loops

So far, we've seen how to use the Python for loop to go over a list or dictionary of items, or a range of numbers. The for loop takes a collection of items and executes a block of code once for each item in the collection. In contrast, the while loop runs as long as, or while, a certain condition is true.

counter = 1 while counter < 5: print(counter) counter += 1

What's the output?


while Loop and Inputs

Why are we talking about the while loop right after our discussion on the input() function? Because more often that not, you want to keep asking the user for information until they decide to end it (think of a chat bot).

prompt = "How can I help you?" message = "" while message != "exit": message = input(prompt) print(message) # do something here with the prompt

Infinite Loops

Infinite loops (or endless loops) are one the scariest things in programming. They run forever and don't let the program to progress to the next part; hence, the program gets stuck in one place. Consider the following code:

while True: message = input("How can I help you?") print(message) # anything below is unreachable print("out of the loop") # rest of the program

Using break to Exit a Loop

In the previous example, we used the conditional test in the first line to exit the loop. Python (and all other popular programming languages) allows you to exit out of a loop at any time using the break keyword.

Let's fix the previous code:

while True: message = input("How can I help you?") if message == "exit": break print(message) # anything below is unreachable print("out of the loop") # rest of the program

Using while Loop with Complex Data Types

You can use the while loop with lists and dictionaries as well.

movies = ["Interstellar", "V for Vendetta", "Kill Bill"] while movies: this_movie = movies.pop(0) print(f"I'd like to watch {this_movie}.")

Best Practice

Avoid using the while loop if you can perform the same task with the for loop. while loops are more error-prone and harder to test. Consider the following code:

movies = ["Interstellar", "V for Vendetta", "Kill Bill"] while movies: this_movie = movies[0] print(f"I'd like to watch {this_movie}.")

What's wrong with it? How can you fix it by replacing the while loop with a for loop?


TRY IT YOURSELF

  • Remove all instances of TikTok from the following list with only one loop.
social_apps = ["TikTok", "Twitter", "TikTok", "Instagram", "Facebook", "TikTok"]
  • Simulate the Internet Explorer browser that asks you Do you want Internet Explorer to be your default browser? repeatedly and doesn't stop until you say Yes or Shut up!.

Files

Learning a programming language is hardly complete without knowing how to handle files. Learning to work with files and save data will make your programs easier for people to use. You will be able to run your program, do some work, and then close the program and pick up where they left off.

Reading from a file is particularly useful in data analysis applications, but it's also applicable to any situation in which you want to analyze or modify information stored in a file.


Reading a File

We can read a file using the pathlib module (or library) that comes with every Python installation. Note that the file has to exist!

from pathlib import Path path = Path("some_file.txt") contents = path.read_text() print(contents)

On Paths

When you pass a simple filename like some_file.txt to Path, Python looks in the directory where the file that's currently being executed (that is, your .py program file) is stored. But that's not always the case.

There are times where the file is located somewhere other than the current directory.


Relative Paths

A relative file path tells Python to look for a given location relative to the directory where the currently running program file is stored.


Relative Paths

my_project ├── my_program │ ├── main.py │ ├── files │ ├── text_file.txt ├── another_file.txt ├── external_files │ ├── yet_another_file.txt

Assuming we're running main.py, here's how we can use relative paths for the .txt files in the above structure:

  • text_file.txt: Path('files/text_file.txt')
  • another_file.txt: Path('../another_file.txt')
  • yet_another_file.txt: Path('../external_files/yet_another_file.txt')

Absolute Paths

You can also tell Python exactly where the file is on your computer, regardless of where the program that's being executed is stored. This is called an absolute file path.

We use absolute paths when a file isn't part of our project structure.


Absolute Paths

Absolute paths are usually longer than relative paths, because they start at your system's root folder (/).

/ ├── home │ ├── mkf │ ├── .zshrc
  • .zshrc absolute path: /home/mkf/.zshrc

Reading a File Line by Line

We can use the splitlines() method on a Path result to read a text file line by line:

from pathlib import Path path = Path("frankenstein.txt") lines = path.splitlines() for line in lines: print(line)

Since splitlines() returns a list of lines, we can use a for loop to run over the lines.


File Content Type

Just like reading a user input, Python treats text file content as string. If you're reading numbers, make sure to cast them to the appropriate type (int or float) before using.


TRY IT YOURSELF

Is your birthday in Pi?

  • Download 1 million digits of Pi from here and save it as a .txt file on your system.
  • Write a program that gets a birthday in the form of DDMMYY from the user (using the input() method) and checks to see if the birthday is included in Pi.

Hint: as a string is just a list of characters, you can use the in keyword to check if a substring exists in a string:

content = "best friend forever" substring = "end" if substring in content: print(f"There's '{substring}' in '{content}'")

Writing to a File

You can the same pathlib, but this time, the write_text() method to write to a file.

from pathlib import Path path = Path('fav_movie.txt') path.write_text("Interstellar")

If the file doesn't exist, write_text() will create the file first. Otherwise, it will erase it before writing new content in it!


TRY IT YOURSELF

  • You can write multiple lines into a file using the write_text() method. Find out how.

  • Write a program that prompts the user for their first name, last name, and date of birth, and writes each piece of data on a new line into a file named after the user's first name (e.g. john.txt)


Exceptions

Python uses special objects called exceptions to manage errors that arise during a program's execution. Whenever an error occurs that makes Python unsure of what to do next, it creates an exception object. If you write code that handles the exception, the program will continue running. If you don't handle the exception, the program will break.


Handling Exceptions

We can handle exceptions using the try-except block. A try-except block asks Python to do something, but it also tells Python what to do if an exception is raised.

When using try-except, the program, instead of breaking and showing the tracebacks, which is bad, users will see friendly error messages that you've written.

There are many situations in which an exception may happen. We'll see a number of them in this course.


Best Practice

When writing a Python program, make sure to read the documentation on the libraries you're using and find out about the potential exceptions that may arise. Make sure you understand them and handle them gracefully in your application.

As an exercise, read the following docs on the pathlib Python module: link and list all the exceptions.


ZeroDivisionError Exception

Try the following program:

print(10/0)

Let's fix it using a try-except block:

try: print(10/0) except ZeroDivisionError: print("You can't divide numbers by 0! Did you skip math class in high school?")

FileNotFoundError Exception

If you try to read a file that doesn't exist, you'll get an exception. You can handle the situation with a try-except block.

from pathlib import Path file_name = input("What file do you want me to read? ") path = Path(file_name) try: contents = path.read_text() except FileNotFoundError: print("Sorry! The file doesn't seem to exist!")

else Block

You can have a third block besides try and except to handle the situation when no exception occurs:

from pathlib import Path file_name = input("What file do you want me to read? ") path = Path(file_name) try: contents = path.read_text() except FileNotFoundError: print("Sorry! The file doesn't seem to exist!") else: print(contents)

Best Practice

The only code that should go in a try block is code that might cause an exception to be raised. Sometimes you'll have additional code that should run only if the try block was successful; this code goes in the else block. The except block tells Python what to do in case a certain exception arises when it tries to run the code in the try block.


Catch All Exceptions

You can catch any exception that may arise in a situation with the Exception type, which is the parent of all Exceptions.

try: print(10/0) except Exception: print("You can't divide numbers by 0! Did you skip math class in high school?")

Best Practice

  • Don't catch Exception as it doesn't show the type of errors that may happen in the code. Using specific Exceptions such as ZeroDivisionError and FileNotFoundError are more readable.
  • Minimize the amount of code in a try-except block. The larger the body of the try, the more likely that an exception will be raised by a line of code that you didn’t expect to raise an exception. In those cases, the try-except block hides a real error.
  • Use else clause to continue the program following a successful try.
  • Use the finally clause to execute code whether or not an exception is raised in the try block. This is often useful for cleanups, like closing a database connection.

Structured Data

Structured data is data that has a standardized format for efficient access by software and humans alike. Depending on the standard, a standard may be more readable for a machine or a human being.

Examples of structured data:

  • CSV (Comma-Separated Values); more readable for machines. e.g.:
    name,age,occupation Ross Geller,32,Teacher
  • XML (Extensible Markup Language); more readable for humans. e.g.:
    <person> <name>Ross Geller</name> <age>32</age> <occupation>Teacher</occupation> </person>

JSON

Or JavaScript Object Notation is another standard for storing and interchanging data that is kind of in the middle in terms of readability. That is, it's readable for humans to understand and for machines to parse and generate.

JSON is built on two structures:

  1. A collection of name/value pairs (think Python Dictionary)
  2. An ordered list of values (think Python List)

Values in JSON

Values in JSON can be one of the following types:

  1. string. e.g. "Pam Beesly"
  2. number. e.g. 5 or 10.6
  3. object (think Python Dictionary)
  4. array (think Python List)
  5. true
  6. false
  7. null

Keys are always string. i.e. in quotes.


JSON Example

{ "name": "Ross Geller", "age": 32, "occupation": "Teacher", "good_at": ["Pivoting", "Science", "Divorce"], "is_on_a_break": true, "children": { "Ben": {"age": 8, "mother": "Carol"}, "Emma": {"age": 1, "mother": "Rachel"} }, "currently_married_to": null, }

Whitespace in JSON

Whitespace is ignored in JSON and doesn't matter. The followings are the same as far as a machine is concerned, but one is obviously more readable for humans.

{"name":"Ross Geller","age":32,"occupation":"Teacher"}
{ "name": "Ross Geller", "age": 32, "occupation": "Teacher" }

Use this website to beautify/uglify JSON documents.


Programming with JSON

JSON is the most popular standard for storing and interchanging data between machines. As such all major programming languages (such as Python) come with built-in support for generating/parsing JSON documents.


Python & JSON

Python has a built-in module (library) to work with JSON named json. We start by importing the module using import json first.

There are two main method for parsing (or reading) and generating (or writing) JSON documents:

  • json.loads() for parsing JSON documents
  • json.dumps() for generating JSON documents

Reading Example

import json document = '{"name":"Ross Geller","age":32,"occupation":"Teacher"}' # this will convert the document into a Python dictionary person = json.loads(document) print(person["name"]) print(person["age"]) print(person["occupation"])

Reading Example

import json document = '["Carol", "Emily", "Rachel"]' # this will convert the document into a Python list the_gellers = json.loads(document) for wife in the_gellers: print(f"Ross has been married to {wife}.")

TRY IT YOURSELF

What happens when you try to parse (or load) invalid JSON? How can you fix it?

document = '["Carol", "Emily", "Rachel"' # this will convert the document into a Python list the_gellers = json.loads(document) for wife in the_gellers: print(f"Ross has been married to {wife}.")

Writing Example

You normally want to turn either a Python list or dictionary into JSON (although you can turn other Python types too, but that's not common as it doesn't really make sense).

import json person = { "name": "Ross Geller", "age": 32, "occupation": "Teacher" } document = json.dumps(person) print(document)

Writing Example

import json person = { "name": "Ross Geller", "is_on_a_break": True, "currently_married_to": None, } document = json.dumps(person) print(document)

What does True and None convert to in JSON? What happened to the trailing , at the end of the last field?


TRY IT YOURSELF

  • Read a JSON document from a file and parse it in a Python program.

  • Write a JSON document to a file in a Python program.

External Modules

External Modules (or libraries or packages) are modules that are not included in the Python standard library and need to be downloaded and installed first before being used.


pip

pip is the package installer for Python. You can use pip to install external modules. pip can install, reinstall, or uninstall Python packages.

pip is part of your Python installation, so if you have Python installed on your system, you should have pip as well.

To make sure, run pip --version or pip3 --version in a terminal.


pip Cheat Sheet

  • Install the latest version: pip install package-name
  • Install a specific version: pip install package-name==1.2.3
  • Upgrade a package to latest version: pip install --upgrade package-name
  • Uninstall a package: pip uninstall package-name
  • List all installed packages: pip list
  • Search for a package: pip search package-name
  • Updating pip itself: python -m pip install --upgrade pip

pip install Examples

  • pip install requests. library for making HTTP requests.
  • pip install tensorflow. library for machine learning stuff.
  • pip install scipy. library for science stuff.
  • pip install numpy. library for working with multi-dimensional arrays and matrices.
  • pip install matplotlib. library for visualization.