Introduction

What is Python?

Python is a programming language. It is a tool that allows us to tell a computer what to do. We can use Python to create websites, games, AI models, and more.

Why Python?

Python is a great language for beginners. It is easy to read and write, and it is used by many companies and organizations around the world. It is also a great language for experienced programmers. It is used by companies like Google, Facebook, and Netflix.

Getting Started

We start by installing Python on Windows in WSL. Then we install VS Code which is a great editor for Python. And we write our first program in Python.

Installing Python

There are few ways to install Python on your system. Here we will cover WSL installation on Windows 11.

Installing Windows Subsystem for Linux (WSL)

WSL gives you a Linux environment running on Windows.

If you already are using Linux or macOS, or you have WSL installed, you can skip this section.

To install WSL, first open PowerShell as an adminstrator. For this, press the Windows key, type powershell, right-click on the Windows PowerShell app and select Run as administrator. Then enter the following command:

> wsl --install

and press Enter. This will install default Linux distribution (Ubuntu). To complete the installation, restart your computer.

Once restarted, open PowerSheel again, this time as a normal user. Then start a Linux session with:

> wsl ~

If you experience any problems, please refer to Microsoft documentation.

Installing Miniconda

Miniconda is a package manager that allows you to install Python and other packages.

Execute the following commands in your terminal:

$ mkdir Downloads
$ cd Downloads
$ wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh

Once download is completed, run the installer, and follow the instructions:

$ bash Miniconda3-latest-Linux-x86_64.sh

To check that installation was successful, run:

$ conda --version
conda 23.3.1 # your version might be different

To prevent Miniconda from activating the base environment by default, run:

$ conda config --set auto_activate_base false

Prepare your environment

It's usual for Python developers to create a virtual environment for each project. This allows you to install different versions of Python and packages for each project.

For this book, we will create a virtual environment called pyintro:

$ conda create -n pyintro python=3.11

When you need to use this environment, you can activate it with:

$ conda activate pyintro

You can check that Python is installed by running:

$ python --version
Python 3.11.3 # your version might be different

You can also start Python shell with:

$ python

it will display the version and a prompt >>> where you can type Python commands.

Python 3.11.3
>>> print(1 + 1)
2

To exit the shell, type exit() or press Ctrl+D.

To deactivate the Conda environment, run:

$ conda deactivate

Installing VS Code

Visual Studio Code (VS Code) is a free and open-source code editor developed by Microsoft.

To install it on your system open the VS Code home page and download the installer for your operating system (we assume, it's Windows 11).

Run the installer, and follow the on-screen instructions.

In case of any problems, please refer to the official installation guide.

Adding Extensions

Visual Studio Code is a very powerful editor, but can be more useful with extensions. Extensions are small programs that add new features to VS Code.

We will need two extensions for our work: one for working with WSL and another for Python development.

First start a WSL session using PowerShell:

> wsl ~

And launch VS Code with the following command:

code

If this is the first time you doing this, it will ask you to install the Remote - WSL extension. Click on the Install button and wait for the installation to complete.

You will need one more extension for Python development. It's called Python and is useful for syntax highlighting and code completion. To install it, press Ctrl+Shift+X to open the Extensions panel, type Python in the search box, and click on the Install button.

Hello World

Create a new directory called pyintro and open it in VS Code.

$ cd ~          # go to your home directory
$ mkdir pyintro # create a project directory
$ cd pyintro    # change the current directory
$ code .        # open VS Code

Create a new file add the following code to it:

File: hello_world.py

print("Hello, World!")

To execute this code, run the following command in the terminal:

$ conda activate pyintro # make sure you are in the right environment
$ python hello_world.py  # run the script

This should print

Hello, World!

to the terminal.

Congratulations, you have just written your first Python program!

Variables and Simple Data Types

In this chapter, you will learn about variables and simple data types in Python. We start our discussion with what a variable is. Then we will learn about strings and numbers and how to work with them. Finally, we will learn about type hints and how to use them in Python. At the very end you will learn how to better document your code using comments.

Variables

In the previous chapter we wrote a simple program that printed a welcome message to the screen.

Now we'll rewrite this program using variables.

File: hello_world.py

message = "Hello, World!"
print(message)

As before, we can run this program by typing,

$ python hello_world.py

in the shell. As before, the program will print the message

Hello, World!

to the screen.

Note: The conda environment, pyintro, should be activated before running the program.

$ conda activate pyintro
$ python hello_world.py

This will be our way of running Python programs in this book. And we won't remind you about it in the future.

So if the program does the same thing as before, what's the point of writing it in a different way? Before we answer this question, let's take a closer look at the program itself.

The first line of the program declares a variable:

message = "Hello, World!"

In Python, variables are created when you assign a value to them using the = operator. Variable has a name and a value. In our case, the name of the variable is message and the value we assign to it is "Hello, World!".

You can think of variables as containers for storing data. We can later use the variable to access the data stored in it, by its name. In our case, we used the variable message to print the message to the screen:

print(message)

The nice thing about variables is that we can continue to use them in other parts of the program. For example, we can print the message again, but this time printing it with uppercase letters. For this, we can add a new line to the program:

print(message.upper())

The output of the program will be

Hello, World!
HELLO, WORLD!

To answer our question from the beginning of this section, the point of using variables is that they allow us to reuse the data stored in them. In our case, we stored the message in the variable message and then used it twice in the program. If we need to change the message, we only need to change it in one place, where we assign the value to the variable. The rest of the program will use the new value automatically.

Type Hits

One important propery of a variable, which might not be obvious from the example above, is its type. In this case, the type of the variable is str, which is short for string. To make this more explicit, we could have declared the variable in the following way:

message: str = "Hello, World!"

(note the : str part after variable name).

This is called type annotation. We will prefer to call it type hint, because it is not enforced by the Python interpreter. Python is a dynamically typed language, which means that you don't have to specify the type of the variable when you declare it. And, event if you do, Python will ignore it.

However, it is a good practice to do so, because it makes the code easier to read and understand. Also VS Code uses type hints to provide additional help when writing code.

Please note, that in the example above there was no need to specify the type of the variable, because Python can infer it from the value we assign to it. We will prefer using type hints, when appropriate or for illustrative purposes.

Strings

In previous section we declared a string variable message and printed it to the screen.

message = "Hello, World!"

What exactly is a string? A string is a sequence of characters. In the example above, the string "Hello, World!" consists of 13 characters: H, e, l, l, o, ,, , W, o, r, l, d, !.

Strings are one of the most common data types that you'll encounter in Python programs. You can use them to represent names, addresses, messages, and many other kinds of information.

Strings are easy to recognize in Python programs. They are enclosed in single or double quotes. In previous example we used double quotes, but single quotes are also valid:

message1 = "Hello, World!"
message2 = 'Hello, World!'
assert message1 == message2 # make sure they are equal

Both declarations are equivalent.

You can use either single or double quotes, but you should be consistent. If you start a string with a single quote, you should end it with a single quote. If you start it with a double quote, you should end it with a double quote.

It's possible to use single quotes inside a string that is enclosed in double quotes, and vice versa. For instance this program

print('I said, "Hello, World!"')
print("He said, 'Hello, Sarah!'")

produces the following output:

I said, "Hello, World!"
He said, 'Hello, Sarah!'

It's also possible to use the same type of quotes inside a string, but you have to escape them with a backslash \, for instance:

"I said, \"Hello, World!\""

To include a backslash in a string, you have to escape it with another backslash:

>>> print("C:\\Users\\John")
C:\Users\John

Some Useful String Methods

Python provides a number of useful methods for working with strings. A method is an action that Python can perform on a piece of data. The dot . after the name of the variable name in the following example tells Python to make the upper() method act on the variable.

name = "Linus Torvalds"
print(name.upper())

This program produces the following output:

LINUS TORVALDS

Similarly, the lower() method changes a string to all lowercase:

name = "Linus Torvalds"
print(name.lower())

with output

linus torvalds

One more method, which we'll use a lot in this book, is title(). It changes each word to title case, where each word begins with a capital letter:

name = "linus torvalds"
print(name.title())

which produces the following output:

Linus Torvalds

String Formatting

Very often you'll want to combine several strings, for example if you have first_name and last_name variables, you might want to combine them into a single string full_name. Python provides several ways to do this. In this section we'll look at the f-string method.

Look at the following example:

first_name = "linus"
last_name = "torvalds"
full_name = f"{first_name} {last_name}"
print(full_name)

and its output:

linus torvalds

First two lines should be familiar to you, we declare two string variables for first and last names. The third line is more interesting

full_name = f"{first_name} {last_name}"

Here, we initialize variable with an f-string. The f stands for format. An f-string is a string that has an f at the beginning and curly braces {} containing expressions that will be replaced with their values.

We can place any valid Python expression inside the curly braces. For example, we can use the title() method to titleize a string:

first_name: str = "linus"
last_name: str = "torvalds"
full_name: str = f"{first_name} {last_name}"
print(f"Hello, {full_name.title()}!")

which outputs a nicely formatted greeting:

Hello, Linus Torvalds!

Dealing with Whitespaces

Whitespace refers to any nonprinting character, 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.

A table below lists some of the whitespace characters:

NameCharacter
Space' '
Tab'\t'
Newline'\n'

And here's an example of using tabs and newlines:

print("Languages:\n\tPython\n\tC\n\tJavaScript")

and its output:

Languages:
	Python
	C
	JavaScript

Quite often you'll want to strip whitespace from strings. For example, you might want to remove extra whitespace from the right side of a name before storing it. Python provides a number of methods for stripping whitespace from strings.

The basic methods for removing excess whitespace are rstrip(), lstrip(), and strip(). The lstrip() method removes whitespace from the left side of a string, rstrip() removes whitespace from the right side, and strip() removes whitespace from both sides.

Try to run the following program:

favorite_language = ' python '

print(f"'{favorite_language}'")          # original string
print(f"'{favorite_language.rstrip()}'") # right strip
print(f"'{favorite_language.lstrip()}'") # left strip
print(f"'{favorite_language.strip()}'")  # strip both sides

and compare the output:

' python '
' python'
'python '
'python'

Removing Prefixes

Another useful method is removeprefix(). It removes a prefix from a string. For example, if you have an url, you can remove ``

url = 'https://dimakura.github.io'
print(url.removeprefix('https://'))

which produces the following output:

dimakura.github.io

You can also use removesuffix() to remove a suffix from a string:

print(url.removesuffix('.github.io'))

with output:

https://dimakura

Or you can use both methods simultaneously:

print(url.removeprefix('https://').removesuffix('.github.io'))

which gives

dimakura

Numbers

In Python we mostly deal with two types of numbers: integers and floating-point numbers.

Integers

Integers are whole numbers, such as 1, 2, 3, etc. Integers can be positive or negative.

Python supports basic arithmetic operations on integers, such as addition (+), subtraction (-), multiplication (*), and division (/).

Tihs is a simple example of using integer operations in Python:

print(2 + 3)
print(2 - 3)
print(2 * 3)
print(3 / 2)

which will print:

5
-1
6
1.5

Note that the division operator (/) always returns a floating-point number, even if the result is a whole number.

Few more useful operations on numbers are the exponentiation operator (**), the modulo operator (%), and the floor division operator (//).

print(2 ** 3)
print(13 % 5)
print(10 // 4)

with output:

8
3
2

Floating-Point Numbers

Any number with a decimal point is a floating-point number. Python supports the same basic arithmetic operations on floating-point numbers as it does on integers.

As an exercise, try to practice operations on floating-point numbers by modifying the examples above.

Type Checking

If you are not sure what type a variable is, you can ask Python to tell you. For example, if you have a variable called x, you can ask Python what type it is by using the type() function:

x = 10
y = 20.0
z = 'hello'

print(type(x))
print(type(y))
print(type(z))

which will print:

<class 'int'>
<class 'float'>
<class 'str'>

Related to type() is the isinstance() function, which can be used to check if a variable is of a certain type:

print(isinstance(x, int))
print(isinstance(y, float))
print(isinstance(z, str))

print(isinstance(x, float))
print(isinstance(y, str))
print(isinstance(z, int))

which will print:

True
True
True
False
False
False

Comments

Comments are lines that exist in a programs that are ignored by Python. Including comments in programs makes code more readable for humans as it provides some information or explanation about what each part of a program is doing.

In Python, the # character indicates the start of a comment. Any characters after the # and up to the end of the line are part of a comment and will be ignored by the Python interpreter.

# this is a comment
message = "Hello, World!" # this is also a comment

Another way to write comments in Python is to use multi-line strings. Python ignores multi-line strings that are not assigned to a variable, so they can be used as comments.

"""
This is a multi-line comment.
Which can span multiple lines.
"""
message = "Hello, World!"

Exercises

1. Ask the user for their name and print it to the screen

Python has a built-in function called input() that will ask the user for some input. The function takes a single argument, which is the prompt to display to the user. The function returns the input as a string.

name = input("What is your name? ")

Using input(), ask the user for their first and last name. Then print their full name to the screen. Account for the possibility that the user might enter their name in all lowercase letters or use extra spaces before or after their name.

2. Ask the user for two numbers and print their sum

Using input(), ask the user for two numbers. Convert the strings to flaot and store them in separate variables. Then print the sum of the two numbers.

Hint: You will need to use the float() function to convert the strings to floats.

3. Extract username and password from a URL

In Python you can assign multiple variables at once by separating them with commas:

For example, the following statement assigns the value 1 to x and the value 2 to y:

x, y, _, _ = 1, 2, 3, 4

Note that we are using _ to ignore the last two values.

You can also split a string into multiple variables by using the split() method. For example, the following statement assigns the value '1' to x and the value '2' to y:

x, y = '1,2'.split(',') # split the string at the comma

Using what you have learned, and given the following string:

url = 'postgres://user1:mysecret@localhost:5432/mydb'

extract the username and password from the string and print it to the screen.

Lists

A list is a collection of items stored in a particular order. You can make a list that includes the letters of the alphabet, the digits, or the names of all of your coworkers.

In this chapter we will learn how to create and modify lists and how to work with the individual elements in a list. We will also learn several useful operations we can perform on lists, such as sorting and reversing.

Creating Lists

To create a list, enclose the elements within square brackets, separated by commas.

For example, let's create a list of weekdays:

File: days.py

days = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']
print(days)

The output looks exactly like the list in the source code. But this is not always the case:

['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']

Accessing Elements

Individual items in our list are called list elements. We can access them by their index. The index of the first element is 0, the index of the second element is 1, and so on.

To continue with our example from the previous section, we add one more line to days.py program:

print(days[0])

which prints the first element of the list:

mon

It would be nice to capitalize the first letter of the day. We can do this by using the title() method:

print(days[0].title())

which looks much better:

Mon

We can now try to print more elements:

print(days[0].title())
print(days[1].title())
print(days[2].title())

which prints:

Mon
Tue
Wed

One neat trick is to use negative indices. The index -1 refers to the last element, -2 refers to the second last element, and so on. Try it youself, by adding few more lines to the days.py program:

print(days[-1].title())
print(days[-2].title())

and check the output:

Sun
Sat

List Type

Remember that we can check the type of a variable by using the type() function. Let's check the type of our list:

print(type(days))

which produces:

<class 'list'>

Python correctly identifies our variable as a list even though we didn't explicitly specify it. We could have been more explicit and specify the type of the variable using type hint:

from typing import List

days: List[str] = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']
print(type(days))

which would again produce the same output as before:

<class 'list'>

We will prefer using type hints in the rest of the book. But remember that they are ignored by Python interpreter, so you can also omit them if you want.

Let's more closely analyze the code fragment above.

On the first list, we import List class from a module called typing.

from typing import List

We'll discuss modules and imports in more details later in this book. For now just remember that modules provide additional functionality to Python programs. We'll be using other classes from typing module throughout the book.

On the next line,

days: List[str] = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']

we declare variable days to be of type List and initialize it with a list of strings. Note that List has additional parameter [str] which means that the list will contain strings. If we wanted to create a list of integers, we would use List[int] instead.

Slicing

We can access multiple elements of a list with slicing.

Here we print the first three elements of the list:

print(days[0:3])

Instead of a single index we now use two indices separated by a colon. This has an effect of producing a new list which contains elements from the first index up to (but not including) the second index. In our example, we get a list which contains elements from index 0 up to (but not including) index 3. Let's check the output:

['mon', 'tue', 'wed']

In the example above we could have omitted the first index and just write days[:3]. This would produce the same result.

print(days[:3])

If we wanted to get a list of remaining days, we could write:

print(days[3:7])

Note that we use 7 as the second index. This is because the second index is not included in the result, and the last index we want to select is 6. The result is:

['thu', 'fri', 'sat', 'sun']

You could also omit the second index and just write days[3:] if you want all remaining elements.

One more trick is to omit both indices. This will produce a copy of the original list:

print(days[:])

which outputs:

['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']

Out of Range Error

If we try to access an element by index which is too large, we get an error:

For example:

days[7] # the last index is 6

produces:

Traceback (most recent call last):
  File "days_typed.py", line 29, in <module>
    days[7] # the last index is 6
    ~~~~^^^
IndexError: list index out of range

"Out of range" error is a common error in programming.

Modifying Lists

It's often necessary to modify lists after they've been created. You can easily add to or remove items from a list, and you can modify individual items in a list as well.

Adding Elements to a List

We begin with a list of favorite programming languages,

languages = ['Python', 'C++']
print(languages)

which produces the following output:

['Python', 'C++']

There are two ways to add a new element into this list. The first is to use the append() method, which adds the new element to the end of the list:

languages.append('Java')
print(languages)

The output is:

['Python', 'C++', 'Java']

You should usually prefer append() to add elements to a list, because it's very efficient.

In comparison, the insert() method is less efficient, but it gives you more flexibiliy. Using insert(), you can add a new element at any position in your list by specifying the index of the new item. For example, to add the new language 'C' at the beginning of the list, you would use the following code:

languages.insert(0, 'C')
print(languages)

which produces the following output:

['C', 'Python', 'C++', 'Java']

If you need to add a new programming language exactly in the middle of the list, you can use the insert() again:

languages.insert(2, 'Rust')
print(languages)

with the result:

['C', 'Python', 'Rust', 'C++', 'Java']

Removing Elements from a List

Now consider a problem when you want to remove an item from a list.

There are two main ways to go about this task, depending on whether you know the position of the item you want to remove or you know its value.

Let's begin with the case where you know the position of the item you want to remove. You can use pop() method for this.

Let's see an example:

numbers = [1, 2, 3, 4, 5]

print(numbers)         # original list
print(numbers.pop())   # removing last element
print(numbers)         # list after pop

which produces the following output:

[1, 2, 3, 4, 5]
5
[1, 2, 3, 4]

Notice the line where we pop()ed the element:

print(numbers.pop())   # removing last element

In this case the last element is removed. And usually, you should prefer to use pop() in this way, because it's very efficient.

There's another way to use pop(). If you want to remove an item from any position in a list, you can use the pop() method with an index. For example, let's remove the first item from our list:

print(numbers.pop(0))  # removing first element
print(numbers)

which produces the following output:

1
[2, 3, 4]

When used with a positional argument, pop() is considerably slower but it gives you more flexibility.

Another method for removing items from a list is remove(). This method is useful when you know the value of the item you want to remove from the list. The remove() method deletes only the first occurrence of the value you specify. Check this in action:

list = [1, 2, 3, 1, 5, 1]
print(list)
list.remove(1)
print(list)

which gives us:

[1, 2, 3, 1, 5, 1]
[2, 3, 1, 5, 1]

Notice that only the first 1 is removed from the list.

We can apply remove() few more times:

list.remove(3)
list.remove(1)
print(list)

which leaves us with:

[2, 5, 1]

Note that the last 1 is still in the list.

If we try to remove element which is not in the list that would produce an error.

Clearing a List

If you want to remove everything from a list, you can use the clear() method. For example,

quarter = ['january', 'february', 'march']
quarter.clear()
print(quarter)

which produces:

[]

Modifying Elements in a List

It's possible to modify elements in a list by assigning new values to them. Let's see an example:

File: modify.py

numbers = [1, 2, 3, 4, 5]
print(numbers)

Here we create a new list of integers and output it to the console:

[1, 2, 3, 4, 5]

We can now update individual elements of the list using assignment operator =:

numbers[0] = 10
numbers[-1] = numbers[-1] * 10
print(numbers)

Note that we update first and last elements of the list. The index-access rules we've seen before apply here as well. The output of the program is:

[10, 2, 3, 4, 50]

It's even possible to modify slices of a list.

numbers[1:4] = [33]
print(numbers)

which replaces three middle elements of the list with a single element:

[10, 33, 50]

del Operator

Python's del operator is used to delete an object. It can be used to delete a variable, a list, or a part of a list (slice).

Deleting a Variable

We've already seen how to declare a variable.

Filename: del.py

a = 10
print(a)

This outputs a number:

10

Actually, we can delete the variable a using the del operator.

del a

Deletion is the opossite of declaration. The variable a does not exist anymore! So, if we try to print it:

print(a)

System will throw an error:

Traceback (most recent call last):
  File "del.py", line 11, in <module>
    print(a)
          ^
NameError: name 'a' is not defined

Delete an Item from a List

You can use del to delete an item from a list. This looks pretty much like pop() method which we discussed in the previous section, but del does not return the deleted item.

Filename: del_list.py

names = ['Guido', 'Jukka', 'Ivan', 'Tim', 'Raymond']
del names[1]  # remove 'Jukka'
print(names)

This outputs:

['Guido', 'Ivan', 'Tim', 'Raymond']

Delete a Slice from a List

del can also be used to delete a slice from a list. This makes del more powerful than pop() method.

Here's an example:

del names[1:3]  # remove 'Ivan' and 'Tim'
print(names)

which gives:

['Guido', 'Raymond']

Length

The len() function returns the number of elements in a list.

File: len.py

cars = ['bmw', 'mercedes', 'audi', 'subaru']
print(len(cars))

which prints length of the list to the console

4

We can use the len() function to determine length of other data types different from lists. For example, we can determine the length of a string.

File: len.py

car = 'bmw'
print(len(car))

produces

3

Sorting

In this section we will use the following list or unordered numbers:

numbers = [7, 8, 1, 6, 5, 4, 3, 2, 10, 9]

What if we want to sort the list in ascending order? There are two ways to sort a list: temporarily and permanently.

During temporary sorting, the original list is not modified and a sorted copy is returned. Temporary sorting is done using the sorted() function.

sorted_numbes = sorted(numbers) # sorted copy
print(sorted_numbes) # print sorted numbers
print(numbers)       # print original list

which gives us:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[7, 8, 1, 6, 5, 4, 3, 2, 10, 9]

Note, that the originl list, numbers, is not modified.

When sorting permanently, the original list is modified. Permanent sorting is done using the sort() method:

numbers.sort() # sort in place
print(numbers)

with the original list changed:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Note, that the in-place, permanent, sorting is more efficient than temporary sorting. On the other hand, temporary sorting is safer because it does not modify the original list.

Reverse Order

You can reverse the order of a list by calling the reverse() method on the list. This method changes the order of the list permanently, but you can revert to the original order anytime by applying reverse() to the same list a second time.

File: reverse.py:

numbers = list(range(1, 6))
print(numbers)

numbers.reverse() # reverse the list
print(numbers)

numbers.reverse() # reverse the list again
print(numbers)

The first print statement outputs the original numbers list.

[1, 2, 3, 4, 5]

Note the way we created this list. range(1, 6) creates an iterator that produces the numbers 1 through 5 (6 is not included). Then the list() function converts the iterator to a list.

The first call to reverse() changes the order of the list permanently. This can be seen in the output:

[5, 4, 3, 2, 1]

The second call to reverse() changes the order of the list back to the original order:

[1, 2, 3, 4, 5]

As it was the case with sorting we can also reverse the order of a list temporarily by using the reversed() class.

reversed_numbers: reversed = reversed(numbers)

One important point here is that reversed() produces an iterator. We can convert it to a list by using the list() function.

If we now print it:

print(list(reversed_numbers))

we get numbers in reverse order:

[5, 4, 3, 2, 1]

Note, that the original list, numbers, is not modified.

In place reversal is usually more efficient than temporary reversal. On the other hand, temporary reversal is safer because it does not modify the original list. In addition, temporary reversal is lazy, meaning it does not perform any work until it is needed.

Exercises

For all exercises below consider the following list is given to you:

fruits = ["apple", "banana", "cherry"]

1. List access

Print the first element of the list. Then print the last element of the list.

2. List modifications

Change the value of the second element of the list to "orange". And add "lemon" to the end of the list.

3. List modifications-2

Add "kiwi" and "lemon" to the list, so that the final list looks like this:

["kiwi", "apple", "lemon", "banana", "cherry"]

4. List sorting

Add "kiwi" and "lemon" at the end to the list.

Sort the list alphabetically.

Now sort the list in descending alphabetical order, so that "kiwi" and "lemon" are the first two elements of the list.

5. List slicing

Print the first two elements of the list.

Print the last two elements of the list.

The output should be:

['apple', 'banana']
['banana', 'cherry']

6. List length

Print the length of the list.

Print the length of the list after adding "kiwi" and "lemon" to the list.

Print number of characters of the first element of the list.

If Statement

Programs that we wrote so far have been executed in a sequential order. This means that each line of code is executed one after another. However, in many cases, we need to execute a block of code only if a certain condition is met. For example, we may want to print a message only if a user is logged in. In this case, we need to use an if statement.

An Example

The following code shows how to use an if statement to check if a person is old enough to vote.

File: vote.py:

age = 33

if age < 18:
    print("Sorry, you are too young!")
else:
    print("You can vote!")

with result:

You can vote!

We will learn more about if statements in this chapter.

Conditional Tests

Before dealing with if statements, we need to understand conditional tests.

Conditional tests are expressions that can be evaluated as True or False. Both True and False are special values that belong to the bool type.

Comparing Strings

We can test whether two strings are equal by using the equality operator ==. Note that the equality operator is different from the assignment operator =.

Let's start with a simple example:

File: strings.py:

car = 'Bmw'
print(car == 'Bmw')

which produces:

True

Here our conditional test evaluates to True because the two strings are equal. If we change the value on the right-side of the equality operator to something else, the test will evaluate to False:

print(car == 'Audi')
# False

Equality operator is case-sensitive. For example, the following test also evaluates to False:

print(car == 'bmw')
# False

This is because variable car contains the string Bmw with a capital B, while the string literal on the right-side of the equality operator contains the string bmw with a lowercase b.

If our intention was to compare the two strings without considering the case, we can use the lower() method to convert variable car to lowercase before comparison:

print(car.lower() == 'bmw')
# True

Checking for Inequality

We can check whether two strings are not equal by using the inequality operator !=:

print(car != 'Bmw')
print(car != 'Audi')
# False
# True

Numerical Comparisons

As with strings, we can compare numbers using the equality and inequality operators:

age = 33
print(age == 33)
print(age != 18)

which prints:

True
True

Additionally, we can use less or greater than operators to check whether a number is less than or greater than another number:

print(age < 40) # less than
# True

print(age > 40) # greater than
# False

Two more operators to compare numbers are <= and >= which check whether a number is less than or equal to, or greater than or equal to another number:

print(age <= 33) # less than or equal to
# True

print(age >= 33) # greater than or equal to
# True

Boolean Variable

One special case of conditional tests is a boolean variable. A boolean variable has type bool and can be either True or False. For example:

breakfast_ready: bool = True
game_over: bool = False

If Statement

Simple if Statement

In it's simplest form, an if statement looks like this:

if conditional_test:
    # do something

if conditional_test evaluates to True, then the indented code block is executed. If it evaluates to False, then the indented code block is skipped.

Python uses indentation to determine which lines of code are associated with the if statement. All indented lines after the if statement are considered part of the code block. The code block ends when the indentation returns to the same level as the if statement.

if conditional_test:
    # do something
    # do something else

# code block ends here

Let's see this in practice:

car = "BMW"

if car.lower() == "bmw":
    print("Your car is awesome!")

print("Have a nice day!")

with output:

Your car is awesome!
Have a nice day!

Try to change the value of car to something else and see what happens.

if-else Statement

A more elaborate form of the if statement is the if-else statement. It looks like this:

if conditional_test:
    # do something
else:
    # do something different

It allows you to specify an alternative action if the conditional_test evaluates to False.

For example,

car = "Mercedes"

if car.lower() == "bmw":
    print("Your car is awesome!")
else:
    print("Your car is okay...")

print("Have a nice day!")

produces output:

Your car is okay...
Have a nice day!

if-elif-else Statement

The if-elif-else statement allows you to specify multiple alternative actions. It's allowed to have multiple elif statements, but only one else statement.

if conditional_test_1:
    # do something
elif conditional_test_2:
    # do something different
elif conditional_test_3:
    # do something event more different
else:
    # do something else

We can now write more ellaborate classification of cars:

car = "Mercedes"

if car.lower() == "bmw":
    print("Your car is awesome!")
elif car.lower() == "mercedes":
    print("Your car is expensive!")
elif car.lower() == "ferarri":
    print("Your car is fast!")
else:
    print("Your car is okay...")

print("Have a nice day!")
Your car is expensive!
Have a nice day!

In this example, once car.lower() == "mercedes" evaluates to True, the rest of the elif statements, and the else statement, are skipped.

The else statement is optional. It is used to specify an action if none of the if or elif statements evaluate to True. And it must be the last statement in the if statement.

Complex Conditions

Python allows you to combine multiple simple conditions into a single complex condition.

and Operator

The and operator evaluates to True if both conditions are True, otherwise it evaluates to False.

>>> True and True
True

>>> True and False
False

>>> False and True
False

>>> False and False
False

Let's check when this might be useful:

age = 20
country = 'Cameroon'

if age < 21 and country == 'Cameroon':
    print('In Cameroon, you must be 21 to vote')
else:
    print('You are eligible to vote!')

which gives:

In Cameroon, you must be 21 to vote

In this example, we specify the value of age to be 20, which is enough to vote in almost any country. But in Cameroon, the voting age starts from 21.

or Operator

The or operator evaluates to True if either of the conditions is True, otherwise it evaluates to False.

>>> True or True
True

>>> True or False
True

>>> False or True
True

>>> False or False
False

Continuing with the previous example, Cameroon is not the only country with a voting age of 21. For example, the same is true for Malaysia. Let's add this to our conditon:

age = 20
country = 'Malaysia'

if age < 21 and (country == 'Cameroon' or country == 'Malaysia'):
    print('In Cameroon and Malaysia, you must be 21 to vote')
else:
    print('You are eligible to vote!')

which gives:

In Cameroon and Malaysia, you must be 21 to vote

Note that we used parentheses to group the conditions. This ensures that the or operator is evaluated first (within parentheses), and then the and operator.

List conditionals

in operator

In previous section we wrote a complex condition which checked if the value of country is Cameroon or Malaysia. But what if we have a list of countries? Actually here's a full list of countries with voting age of 21:

['Cameroon', 'Malaysia', 'Oman', 
 'Samoa', 'Singapore', 'Tokelau',
 'Tonga']

We could persue our previous approach and write a complex condition using or operator. That would require us to write a lot of code. But there's a better way. We can use the in operator to check if a value is in a list. Our program would look like this in such case:

age = 20
country = 'Singapore'
countries_21 = ['Cameroon', 'Malaysia', 'Oman',
                'Samoa', 'Singapore', 'Tokelau',
                'Tonga']

if age < 21 and country in countries_21:
    print(f'In {country}, you must be 21 to vote')
else:
    print('You are eligible to vote!')

which produces:

In Singapore, you must be 21 to vote

not in operator

We can also check if a value is not in a list using not in operator. Here's an example:

awesome_cars = ['BMW', 'Porsche']
expensive_cars = ['Mercedes', 'Rolls Royce']
fast_cars = ['Ferrari', 'Bugatti']

car = 'Toyota'

not_awesome = car not in awesome_cars
not_expensive = car not in expensive_cars
not_fast = car not in fast_cars

if not_awesome and not_expensive and not_fast:
    print('This car is boring!')

which indeed tells us:

This car is boring!

Check if list is empty

If we want to check if a list is empty we can use the fact that empty lists are considered False in Python. Here's an example:

cars = []

if cars:
    print(f'You have {len(cars)} cars!')
else:
    print('You have no cars!')

This produces:

You have no cars!

If you add few more cars to the list

cars = ['BMW', 'Porsche']

the output will change to:

You have 2 cars!

Negation

Negation or not is a logical operator that inverts the truth value of its operand.

>>> not True
False

>>> not False
True

This operator is very useful in certain contexts.

Exercises

1. Student scores

Ask a student for their name and their score. Print out their name and their score. If their score is greater than 85, print out "Distinction". If their score is between 65 and 85, print out "Pass". Otherwise, print out "Fail".

2. List conditionals

Create an empty list called fruits. Ask the user to enter his favourite fruit and add it to the list. Now ask the user to enter his second favorite fruit and add it to the list. If the fruit he enters is already in the list, print "You already picked that fruit!". When finished entering fruits, print out the list.

3. FizzBuzz

Ask the user for a number. If the number is a multiple of 3, print "Fizz". If the number is a multiple of 5, print "Buzz". If the number is a multiple of both 3 and 5, print "FizzBuzz". Otherwise, print the number.

Loops

Loops are very useful when you need to perform a task over and over again, such as processing a list of items or performing a task for a given number of times.

For Loop

Let's start with an example.

Suppose we have a list of animals and we want to great each one of them. To begin with, we'll simply print each animal:

animals = ['cat', 'dog', 'monkey']

for animal in animals: # assign next value to animal
    print(animal)      # print animal

The first line of this program should be familiar by now; it creates a list of three animals.

What happens next is the interesting part. We use a for loop to iterate over the list of animals. Each time the loop executes, the variable animal will be assigned the next value in the list. The first time through the loop, animal will be assigned the value 'cat', the second time through the loop, animal will be assigned the value 'dog', and the third time through the loop, animal will be assigned the value 'monkey'. The loop will terminate when there are no more values in the list.

At every step the loop performs the same action: it prints the value of animal. That's easy to see from the output of the program:

cat
dog
monkey
Hello, Cat!
Hello, Dog!
Hello, Monkey!
Hello, Monkey!

Note that the action performed by the loop is indented. This is a requirement in Python. The indentation tells Python which statements are part of the loop. In this case we have a single statement, the print statement, but we could have had several statements, all indented to the same level.

An example of a program with few statements is given below:

for animal in animals:
    greeting = f"Hello, {animal.title()}!"
    print(greeting)

with the output:

Hello, Cat!
Hello, Dog!
Hello, Monkey!

If we don't indent the second statements in the loop, we won't get any error. This still will be a valid Python program, but it will do something different:

for animal in animals:
    greeting = f"Hello, {animal.title()}!"
print(greeting)

The result of executing this program will be a single line, with the last value of greeting:

Hello, Monkey!

Range Loop

It's a very common task to loop over a range of numbers. Python provides a built-in function called range that generates a sequence of numbers. range can accept one, two, or three parameters.

All three of the following function calls will generate the same sequence of numbers (0 through 9):

range(10)
range(0, 10)
range(0, 10, 1)

The sequence (or, in Python parlance, iterator) generated by range is not a list. But we can still use it in a for loop, as we used a list in the previous section.

Let's see this in action. We'll use a for loop to print the numbers from 0 to 9:

for num in range(10):
    print(num, end=' ')

which produces the following output:

0 1 2 3 4 5 6 7 8 9 

If you need to loop over millions of numbers, range is a better choice than creating a list of numbers, because it takes up considerably less memory.

Let's see a more elaborate example of using range to create list of squares of numbers from 1 to 10:

squares = []

for number in range(1, 11):
    square = number ** 2
    squares.append(square)

print(squares)

In this program we first create an empty list of squares. Then we loop over the numbers from 1 to 10 and append the square of each number to the list. Finally, we print the list.

Here's the output of the program:

[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

Take time to study this program and make sure you understand how it works. The best way to do this is to run the program and experiment with it by introducing small changes and observing the output. If at a particular run you get an error, try to understand the error message and fix the error.

Stats

Loops can be used to calculate statistics of a list (or an iterator) of numbers. Let's see how we can calculate the sum, minimum, and maximum of a list of numbers.

Sum

To calculate sum of a list of numbers, we can use a for loop to iterate over the list and add each number to a variable that keeps track of the sum. Here's an example:

sum_of_numbers = 0

for num in range(1, 101):
    sum_of_numbers += num

print(f"Sum: {sum_of_numbers}")
print(f"Average: {sum_of_numbers / 100}")

Not that at the last line we also calculate the average of the numbers by dividing the sum by the length of the list.

Output of the program:

Sum: 5050
Average: 50.5

Minimum and Maximum

Here's how we can calculate the minimum and maximum of a list of numbers:

numbers = [10, 6, 2, 3, 9, 0, 5, 7, 1, 4, 8]

min = numbers[0]
max = numbers[0]

for number in numbers:
    if number < min:
        min = number
    if number > max:
        max = number

print(f"Min: {min}")
print(f"Max: {max}")

We first initialize the minimum and maximum to the first element of the list. Then we iterate over the list and update the minimum and maximum if we find a smaller or larger number. Finally, we print the minimum and maximum:

Min: 0
Max: 10

Notice how we use the if statement inside the for loop.

Built-in Functions

In practice, we don't need to write our own functions to calculate the sum, minimum, and maximum of a list of numbers. Python provides built-in functions for these purposes. Here's how we could use them:

numbers = [1, 2, 3, 4, 5]

print("Sum:", sum(numbers))
print("Min:", min(numbers))
print("Max:", max(numbers))

and here's the output:

Sum: 15
Min: 1
Max: 5

In general, in programming it's a good idea to use built-in functions whenever possible. They are usually faster and more reliable than our own functions.

Comprehensions

List comprehensions are a way to create lists in a more concise way. They are a bit more advanced, so don't worry if you don't understand them right away.

In previous section we wrote a loop to create a list of squares. Here's how we can do the same thing with a list comprehension:

squares = [x ** 2 for x in range(1, 11)]
print(squares)

which outputs, as expected:

[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

If you look carefully, list comprehensions are a bit like a reversed for loop.

While Loop

For-loops are great for iterating over a fixed number of elements. But what if you don't know how many elements you need to iterate over? In this case, you can use a while-loop.

Syntax of a while loop:

while condition:
    # do something

The body of the while loop will be performed until condition is satisfied.

Let's see an example:

num = 1

while num < 10:
    print(num, end=' ')
    num += 1

Here, we start with the value of num be equal to 1. We first check that the value of num is less than or equal to 10. If this is true, we print the value of num and then increment it by 1 (notice +=). Thus, on the second iteration, the value of num will be 2. This process will continue until the value of num is 10. At this point, the condition num < 10 will be false and the while loop will terminate.

Therefore, the output should be the numbers from 1 to 9 (but not 10):

1 2 3 4 5 6 7 8 9 

Exercises

1. Squares

Ask user to enter five numbers between 1 and 10. If user enters number outside of that range, print message "Number is not in range". When user enters all five numbers, print squares of all numbers.

2. Sum of numbers

Create a list of numbers by asking user to enter numbers. When user enters "stop", stop asking for numbers. Print sum of all numbers in the list. Don't use sum() function.

3. Factorial

Ask user to enter a number. Print factorial of that number. If user enters number less than 0, print message "Number is not in range".

Note: factorial of 0 is 1.

4. Fibonacci

Fibonacci sequence is a sequence of numbers where each number is a sum of two previous numbers. First two numbers are 0 and 1. For example, first ten numbers in Fibonacci sequence are:

0, 1, 1, 2, 3, 5, 8, 13, 21, 34

Print first n numbers in Fibonacci sequence, where n is a number entered by user.

Collections

We already saw lists, which are a collection of elements. In this chapter we will see other collections: tuples, dictionaries and sets. We will also see how to create collections using comprehensions.

Tuples

Tuple is a collection of items of any type. It is immutable, which means that once created, it cannot be modified. Otherwise, it is very similar to a list.

As usual, let's start with a simple example:

fruits = ("apple", "banana", "cherry")
print(fruits)

which produces the following output:

('apple', 'banana', 'cherry')

You can access tuple items by index, just like with lists:

print(fruits[0])
print(fruits[1])
print(fruits[2])

which gives:

apple
banana
cherry

As with lists, you can use negative indices to access items from the end of the tuple:

print(fruits[-1])

which prints:

cherry

But unlike lists, tuples are immutable, which means that you cannot modify them. For instance, if you try to assign a new value to an item:

fruits[0] = "pear"

you will get an error:

Traceback (most recent call last):
  File "tuples.py", line 17, in <module>
    fruits[0] = "pear"
    ~~~~~~^^^
TypeError: 'tuple' object does not support item assignment

Tuple methods

To check if an item is in a tuple, you can use the in operator:

numbers = (1, 2, 3, 1)

print(1 in numbers)
print(4 not in numbers)

which produces:

True
True

Note also that in the example above, we have the value 1 twice in the tuple. Tuples, as lists, can contain duplicate items.

To obtain size of a tuple, you can use the len() function:

print(len(numbers))
# => 4

Tuple type

To see type of the tuple, you can use the type() function, as usual:

print(type(numbers))
# => <class 'tuple'>

One interesting case of a tuple is a tuple with a single item. In this case, you need to add a comma after the item, otherwise Python will treat it as a regular value in parentheses:

single_value = (1)
print(type(single_value))

a_tuple = (1,)
print(type(a_tuple))

which prints:

<class 'int'>
<class 'tuple'>

When to use tuples over lists

Prefer tuples over lists when you want to make sure that the collection is not modified. Tuples are generally faster and more memory efficient than lists.

Dictionaries

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.

Creating a Dictionary

To define a dictionary, you use curly braces {} to indicate the beginning and end of the dictionary, and use : to separate keys and values. A simple dictionary looks like this:

languages = {'Python': 'Readable', 'C++': 'Fast', 'Java': 'Reliable', 'Rust': 'Safe'}
print(languages)

which produces the following output:

{'Python': 'Readable', 'C++': 'Fast', 'Java': 'Reliable', 'Rust': 'Safe'}

Note that after Python 3.6, the order of the key-value pairs is preserved. In earlier versions of Python, the order of the key-value pairs is not preserved.

Also note that the keys in a dictionary must be unique. If you use the same key twice, the second key-value pair will overwrite the first one.

Accessing Values

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 []. For example, the following snippet

print(languages['Python'])
print(languages['C++'])
print(languages['Java'])
print(languages['Rust'])

produces an output:

Readable
Fast
Reliable
Safe

If we try to access a key that doesn't exist in a dictionary

print(languages['C'])

Python will raise an error:

    print(languages['C'])
          ~~~~~~~~~^^^^^
KeyError: 'C'

If this is an issue, there's another way to access a value in a dictionary. The get() method returns None if the key doesn't exist in the dictionary. You can also specify a default value to use if the key doesn't exist.

print(languages.get('Python'))     # same as languages['Python']
print(languages.get('C') is None)  # key "C" does not exist
print(languages.get('C', 'Fun'))   # returns "Fun"

If you need to check whether dictionary has a particular key, you can use the in operator.

print('Python' in languages) # True
print('C' in languages)      # False

Looping Through a Dictionary

It's very common to loop through all key-value pairs in a dictionary.

Below is an example of using for loop. Notice that the language variable gets the successive keys in the dictionary as the loop moves through the dictionary.

for language in languages:
    print(f"{language} is {languages[language].lower()}")

And this the output:

Python is readable
C++ is fast
Java is reliable
Rust is safe

Because it's common to use both key and value of a dictionary while looping through it, Python provides a special method called items() that returns a list of key-value pairs. In the following example, we use items() to loop through the dictionary and do the same thing as the previous example.

for language, quality in languages.items():
    print(f"{language} is {quality.lower()}")

The keys() method returns a list of all keys in a dictionary. The values() method returns a list of all values in a dictionary. These two methods can be used in variour ways. For example, you can loop through keys in sorted order:

for language in sorted(languages.keys()):
    print(f"{language} is {languages[language].lower()}")

Which produces almost the same output as the previous examples, except that the languages are now sorted:

C++ is fast
Java is reliable
Python is readable
Rust is safe

Change Dictionaries

It's fairly easy to change dictionaries. You can add new key-value pairs, modify existing key-value pairs, and remove key-value pairs.

The following example creates an empty dictionary and adds key-value pairs to it. In tnis case we add names of students as keys and their yearly points as values.

students = {}

students['Alice'] = 20 # {'Alice': 20}
students['Bob'] = 19   # {'Alice': 20, 'Bob': 19}

Another way to add or change key-value pairs in a dictionary is to use the update() method. The update() method takes a dictionary as an argument and adds all key-value pairs from that dictionary to the current dictionary. If the key already exists, the value associated with that key is replaced.

students.update({'Alice': 22, 'Charlie': 25, 'Daniel': 23})

If we print students dictionary now, we'll see that the argument dictionary was added to the students dictionary.

{'Alice': 22, 'Bob': 19, 'Charlie': 25, 'Daniel': 23}

If we need to remove items from a dictionary, there are few ways to do it. The popitem() mehtod removes the last key-value pair from a dictionary and returns it as a tuple.

print(students.popitem())
# => ('Daniel', 23)

The pop() method removes a key-value pair from a dictionary and returns the value associated with the key. If the key doesn't exist, the method returns the default value specified as the second argument (or fails with error).

print(students.pop('Bob', 33)) # => 19 (Bob's points)
print(students.pop('Bob', 33)) # => 33 (default value)

The del statement removes a key-value pair from a dictionary.

del students['Charlie']

And, finally, the clear() method removes all key-value pairs from a dictionary.

Sets

Sets are much like lists, but they can only contain unique values. Also, sets are unordered, so you cannot access them by index.

Creating a set

To create a set you can use the {} syntax.

fruits = {"apple", "banana", "cherry"}

Another way would be to use the set() function. When using the set() function, you can pass in an iterable, like a list, as an argument.

fruits_from_list = set(["apple", "banana", "cherry"])

In both examples we will get the same result:

print(fruits == fruits_from_list)
# => True

The set function can also be used to create an empty set:

empty_set = set()
print(len(empty_set))
# => 0

We canno use the {} syntax to create an empty set, because it would create an empty dictionary instead.

Accessing set items

We cannot access set items by index, because sets are unordered.

But we can loop over the set and print each item:

for fruit in fruits:
    print(fruit)

which produces (not necessarily in this order):

0
apple
banana

We can also test if an item is in a set:

print("banana" in fruits)
# => True

print("pineapple" in fruits)
# => False

Add items to a set

To add items to a set, we can use the add() method:

fruits.add("pineapple")
# => {"apple", "banana", "cherry", "pineapple"}

print("pineapple" in fruits)
# => True

Another way would be to use the update() method, which takes an iterable as an argument:

fruits.update(["orange", "mango", "grapes"])
# => {"apple", "banana", "cherry", "pineapple", "orange", "mango", "grapes"}

Remove items from a set

You can use the remove() method to remove an item from a set:

fruits.remove("banana")
# => {"apple", "cherry", "pineapple", "orange", "mango", "grapes"}

If you need to remove any item from a set, you can use the pop() method:

fruit = fruits.pop()
print(fruit)
# => "pineapple" or any other element

Set operations

Sets have a lot of useful methods to perform set operations.

Union

To get the union of two sets, you can use the union() method:

{1, 2, 3}.union({3, 4, 5})
# => {1, 2, 3, 4, 5}

Or, alternatively, you can use the | operator:

{1, 2, 3} | {3, 4, 5}
# => {1, 2, 3, 4, 5}

Intersection

To get the intersection of two sets, you can use the intersection() method:

{1, 2, 3}.intersection({3, 4, 5})
# => {3}

Or, alternatively, you can use the & operator:

{1, 2, 3} & {3, 4, 5}
# => {3}

Related to intersection are the isdisjoint() and issubset() methods. For example,

{1, 2, 3}.isdisjoint({10, 11, 12})
# => True (because there are no common elements)

{1, 2, 3}.isdisjoint({3, 4, 5})
# => False (because 3 is in both sets)
{1, 2, 3}.issubset({3, 4, 5})
# => False (because not all elements of the first set are in the second set)

{1, 2, 3}.issubset({1, 2, 3, 4, 5})
# => True (because all elements of the first set are in the second set)

Difference

You can get difference of two sets using the difference() method:

{1, 2, 3}.difference({3, 4, 5})
# => {1, 2}

Note that the operation is not commutative:

{3, 4, 5}.difference({1, 2, 3})
# => {4, 5}

Alternatively, you can use the - operator:

{1, 2, 3} - {3, 4, 5}
# => {1, 2}

Comprehensions

In previous chapter we have seen how to use list comprehension to create lists. Comprehensions can also be used to create tuples, dictionaries, and sets.

Dictionaries

>>> {x: x*x for x in range(6)}
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

Sets

>>> {x*x for x in range(6)}
{0, 1, 4, 9, 16, 25}

Tuples

In case of tuples, we need to use tuple function to convert the generator to tuple.

>>> (x*x for x in range(6),)
<generator object <genexpr> at 0x7f8b1c0b3f10>

>>> tuple(x*x for x in range(6))
(1, 4, 9, 16, 25)

Exercises

1. Student grades

Create an empty dictionary grades. Ask user to enter name of a student and his grade. Add student name and grade to the grades dictionary as a key-value pair.

Ask user if he wants to enter another student. If he enters "yes", repeat the process, otherwise print the grades dictionary and exit the program.

If user enters grade less than 1 or greater than 5, print message "Grade is not in range".

If user enters name of a student that already exists in the list, print message "Student already exists". Give an option to the user to overwrite existing grade or to enter new name.

2. Student grades 2

Rewrite program from the previous exercise but use a list instead of a dictionary. Put student name and grade in a tuple and append it to the list.

3. Job match

We have a list of requiements for a job:

requirements = ["Python", "C++", "Math"]

Allow user to enter his skills into another list.

Now compare list of skills which user has to the list of requirements. Print message "You are hired" if user has at lease two matching skills, otherwise print message "Sorry, you are not hired".