0% found this document useful (0 votes)
10 views

Computer Science for Kids Learning by Doing

The document serves as an introductory guide to programming with Python, explaining how to communicate with computers using programming languages. It covers the installation of Python, writing simple programs, using functions, data types, and control structures like if statements. Additionally, it introduces data structures and loops to manage multiple inputs effectively.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views

Computer Science for Kids Learning by Doing

The document serves as an introductory guide to programming with Python, explaining how to communicate with computers using programming languages. It covers the installation of Python, writing simple programs, using functions, data types, and control structures like if statements. Additionally, it introduces data structures and loops to manage multiple inputs effectively.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 69

To Maria

Table of Contents

Talking to a computer

Looking inside a computer

How computers talk to each other

Making our programs easier to use

How does the Internet work

That's just the beginning

Appendix A - Getting setup

Appendix B - Solutions to extra challenges


Talking to a computer

Computers are our friends. They can help us complete many tasks that we would find very hard to
complete otherwise. However, we need to find a way to tell them how to complete each task. In other
words, we need to find a way to "talk" to a computer. In this chapter, you will learn how you can talk
to a computer. If you didn't know how to do that already, you would probably imagine that would be
a quite difficult thing to do. You will probably be surprised by how easy that can be in practice.

Of course, there are many different languages we can use to talk to computers. This is similar to how
there are many different languages people can use to communicate with each other, such as English,
French, Chinese etc. The languages that we can use to talk to a computer are typically called
programming languages, because they help us program the computer to do what we want. Among all
those languages that are available, in this book you will learn how to use a language called Python.
Before you are able to talk to our computer, first you will have to make sure your computer knows
how to speak the language you want to use. In other words, you will need to "install" a programming
language.

In order to install Python, you can visit the following page, click on the "Download Python" button
and then click on the downloaded file and follow the instructions: https://www.python.org/downloa
ds.
Make sure you download version 3 of Python as this is used throughout this book - this is any version
that starts with 3, such as 3.5, 3.6, 3.7 etc. After the installation is finished, you can make sure it has
been completed successfully and your computer can now "speak" Python. In order to do this quickly,
you need to open a terminal window first and type python3 - in Windows, you need to type py,
instead. If you don't know how to open a terminal, follow the instructions in Appendix A. You should
now see something like the following in your terminal:

$ python3
Python 3.9.0 (default, Dec 3 2020, 16:09:02)
[Clang 12.0.0 (clang-1200.0.32.27)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>

This means your computer is waiting for your instructions. Let's try and give a simple instruction to
the computer, such as adding up two numbers. You can just type a mathematical expression that's
adding two numbers and press Enter. Your computer will perform the necessary calculations and
show you the result:

>>> 40 + 2
42

That's cool, but we could also use a calculator to do that, right? Computers can do much more
complicated things and this is what you will learn in this book. Let's go and write the simplest
program we could think of, a program that says "Hello World!". In order to do that, we will need to
write the instructions of the program in a text file. To make this easier, you could use a text editor,
which is a program that helps you read the contents of a file and change them. One such program is
Sublime, which you can download easily by visiting the following web page and clicking on the
button that says "Download": https://www.sublimetext.com/

Ok, now that we have downloaded a text editor, we can create our first program. First, I want you to
go in your Desktop and create a new folder, called "programs". If you don't know how to do this, see
Appendix A. We will store here all the code for our programs. Open your text editor and create a new
file. When your new file is open in the editor, you can type the following code inside the file:

print("Hello World!")
Before you can run this program, you will need to save the file. If you are using Sublime, you can do
this by selecting the following options on the menu: File > Save and then select the folder where the
file will be stored (the "programs" folder you previously created) and the name of the file (it should be
"first_program.py"). Your first program is now ready! It is time to ask your computer to run it. In order
to do this, you need to open a terminal again, but this time you should open it inside the folder you
created. If you don't know how to do this, you can follow the steps in Appendix A. When the terminal
is open, you can ask your computer to run your program. You can do this by typing python3
first_program.py in the terminal and then pressing Enter. You should see the following in the
terminal:

$ python3 first_program.py
Hello World!

You can see your program talking! Before we move on, let's try and understand the meaning of what
you typed. The first part of the instruction (python3) meant that your computer should run a Python
program and the second part (first_program.py) is the name of the file, where the program is
written. This is needed so that the computer knows where to go and find the program.

Ok, you have now managed to write your first program and ask your computer to run it. However,
your program still doesn't seem very cool. In order to build cool programs, you will have to learn how
to speak Python a bit better. There are many many great things we can do in Python, so we will focus
on learning the most basic things now.

As you saw previously, Python can print things in a terminal. Instead of using a single text inside
quotes, we can also combine multiple texts by adding them to each other with the + operator. For
example, you could replace print("Hello World!") with print("Hello" + "World!") in your
program and it would still work fine. We can also ask the user to give us some information in the
terminal, if we want. This can be done by using the command input followed by the text that will be
shown to the user enclosed in parentheses. We can also store the information provided by the user in a
variable, so that we can use it later. You can think of a variable as a box with a label that can hold some
data inside. Whenever we want to use the data contained in this box (variable), we can refer to them
using the label of the box (the variable name).

Let's try and use this to make our program a bit cooler. We can ask the user to give us their name and
then say hello to them using their name. You can open the file that contains our first program and
change the contents to the following (remember to save the file again!):
name = input("Give me your name: ")

print("Hello " + name + "!")

If you run the program again, you will see the terminal printing "Give me your name: " and then
waiting. After you type your name and press Enter, the program continues and greets you.

$ python3 first_program.py
Give me your name: Dimos
Hello Dimos!

Hopefully, what is going on here is clear. The data the user provides in the terminal (e.g. "Dimos") is
stored in the variable name and the message printed to the console is reading the value of this variable
to generate the message to be printed ("Hello" + name + "!" → "Hello" + "Dimos" + "!" →
"Hello Dimos!").

The input and print instructions we used are called functions. A function is a block of code that can
receive some data as input, perform a specific task and it can also return some data as a result, if
needed. Everytime, you need to execute this specific task, you can just call this function using its
name, instead of rewriting all the code again. As a result, functions can help you organise your code
into small blocks you can reuse in different places. For example, we could create a small function that
takes as input the name of the user and returns the full message text. Our program would look like the
following:

def create_message(name):
message = "Hello " + name + "!"
return message

name = input("Give me your name: ")


message_text = create_message(name)
print(message_text)

If you try and run this program, you will notice the result is exactly the same. What is different is we
defined a function (create_message) and used it to create the message that was printed.

In order to use a function, you first need to define what the function will do. This is what is done at the
top of the program. The definition of a function starts with the def keyword, followed by the name of
the function (create_message here) and the input to the function which can be one or more variables
(here it is just one: name) enclosed in parentheses. This is then followed by a list of instructions that
will be run by the computer every time you use the function. The return instruction is a special
instruction that terminates the function and returns the data contained in a variable. You might have
noticed we used some functions (print and input) before without defining them. This is because the
language contains a group of basic functions that are already defined and can be used immediately by
programmers and these are some of them. There are two ways you can use a function. You can just
type the name of the function and the input variables - e.g. print("Hello World!") - or you can
create a variable that will contain the data returned from the function - e.g. name = input("Give me
your name: "). That is a lot of information, so I tried to summarise it for you in the following picture.
Of course, there are many different types of data a program can handle. So far, we have only seen a
single type, text that consists of a sequence of characters. This type of data in programming is typically
called a string. Some other basic data types are the following:

integers, e.g. -10, 0, 25 etc..


real numbers, also known as floating numbers or floats, e.g. 1.4, 0.3 etc.
booleans, which can take two values True and False.

Sometimes, we might have to convert data from one type to another. Sometimes, this is done
automatically, but Python also provides specific functions to do this, when needed. For example, if we
have a variable age that contains the age of a person as an integer and we want to convert it into a
string in order to print it in the console, we can use the str function, e.g. str(age). If we want to do
the opposite and convert a variable weight that contains a user's weight as a string into an integer to
make calculations, we can do so using the int function, e.g. int(weight).

There are cases where a program has to do different things depending on the value of some data. One
way to achieve this is by using the if statement. This comes in many different flavors:

A simple if statement will execute some piece of code only if some condition is true. Its structure is
the following:

if <condition>:
<code to execute>

An if-else statement will execute some piece of code if some condition is true. Otherwise, it will
execute a different piece of code. Its structure is the following:

if <condition A>:
<code to execute>
else:
<other code to execute>
An if-elif statement will execute different pieces of code depending on which of the specified
conditions is true and will execute a different piece of code if none of them is true. Its structure is
the following:

if <condition A>:
<code to execute>
elif <condition B>:
<other code to execute>
else:
<other code to execute>

Warning: In Python, indentation matters! So, be careful and insert a tab (or the same amount of space)
as shown above. Otherwise, the computer will not be able to understand what the program says and
will complain. In this case, the error might look something like the following:

^
IndentationError: expected an indented block

We have also seen how to assign a value to a variable already by using the = operator, e.g. name =
"Dimos". We can also check if a variable has a specific value in order to decide what action to take in a
program. We can do this by using the == operator, e.g. name == "George". We can either store the
result of this in a separate variable (e.g. is_george = name == "George") or use it directly inside an
if statement as a condition (e.g. if name == "George"). We can also compare different variables
with each other using other operators, such as < (less than) and > (greater than). For example, if we
have two variables with the age of two students maria_age and george_age, we can check if Maria is
older than George in the following way: maria_age > george_age.

Let's go and use all of the cool stuff you learned to make your program a bit more complex. I want you
to change the program, so that it asks the user to also provide the number of programs they have
written (top tip: you will need to convert the user's data from string to int for this). Depending on the
number of programs, they have written, the program will display a different message.

If the user specified a negative number, the program should print "Come on! This is impossible."
If the user gave the number zero, the program should print "It's not that hard! Give it a try."
If the user gave a positive number, the program should print "Well done! Keep practicing."

When you execute the program, it should look like the following:

$ python3 first_program.py
Give me your name: Dimos
How many programs have you written so far: 3
Hello Dimos!
Well done! Keep practicing.

Let's pause now. I want you to try and write the program on your own. If the program contains
syntactic errors, the computer might complain when you try to run it. Don't give up immediately
though, try and see if you can understand what's wrong by reading the error message of the computer
and fix your program.

Done? Ok, here's the solution:


name = input("Give me your name: ")
programs_as_string = input("How many programs have you written so far: ")
programs = int(programs_as_string)

print("Hello " + name + "!")

if programs < 0:
print("Come on! This is impossible.")
elif programs == 0:
print("It's not that hard! Give it a try.")
else:
print("Well done! Keep practicing.")

Your program starts becoming a bit more useful now, right? But, it can still only handle data for a
single user. What if we wanted to receive data for many students, say for the students of a whole class?
If that was possible, we would be able to do cool things, like finding out the best and the worst
programmer in the class!

You will now learn two additional concepts that will help you achieve that:

data structures
loops

Data structures are collections of values in a specific form that allows you to access the data included
in them in different ways. If we go back to our previous example, imagine if we had to handle data for
many students, instead of just one. We would probably need to save the data for them and we could
do that using different variables. For example, if we wanted to process data for 3 students we could do
it in the following way:

first_name = input("Give me the name of the first student: ")


second_name = input("Give me the name of the second student: ")
third_name = input("Give me the name of the third student: ")

If we wanted to greet all of them, we could do the following:

print("Hello " + first_name + ", " + second_name ", and " + third_name)

However, this would become uglier and harder to code the more students the program needed to
handle. For example, imagine how the program would look like if it needed to handle 100 students in
this way. Another problem is the program might not know the number of students in advance. For
example, the program might have to ask the user for the number of the students in the class, before
asking for their names. This is where data structures and loops can help:

Data structures allow a program to store many values in a single variable.


Loops allow a program to perform the same task again and again.

Let's look at our first data structure, the list. A list is an ordered sequence of values. You can create a
list by writing all its values enclosed in square brackets and separated by commas. Here is an example
showing how you could create a list containing some student names and assign it to a variable:
student_names = ["George", "Maria", "Alex"]

Visually, you could think of this variable in the following way.

You can do many interesting things with the list now:

You can easily calculate the size of the list using the len function, e.g. len(student_names) would
have the value 3, since there are 3 values in the list.
You can retrieve an element at a specific location in the list. You can do this using the variable name
followed by square brackets and the position you are looking for. As you can see in the picture
above, the position of the first value is zero and then it increases by one for every value. So,
student_names[1] would return the second value in the list, which is Maria.
You can also add new values in the list by typing the name of the variable, followed by .append()
and the value you want to add inside the parentheses. For example, if you want to add the value
Helen at the end of the list you can do student_names.append("Helen").

Now that we have a list, we might want to repeat a specific task for each value in the list. For example,
we might want to greet each student separately. A for loop statement can help us achieve this. Its
structure is the following:

for <value> in <list>:


<do something>

You can specify a list you want to iterate over (<list>) and the name of the variable that will contain
one value at a time (<value>) and the instructions included in the following block will be executed
one time for each value in the list. An example will probably help you understand this better. If you
want to greet all the students with their names included in the student_names list, you could do that
in the following way:

for name in student_names:


print("Hello " + name + "!")

In some cases, you might not have a list of items and you might just need to perform the same task for
a number of times. You can also do that using a for loop and the range() function in the following
way:
times = 10
for number in range(times):
print("This is number: " + str(number))

What is happening here is the program is going through the numbers in the list [0, 1, 2, 3, 4, 5,
6, 7, 8, 9].

You have now learned how to use data structures (lists, specifically) and for loops. We can use this to
make our program a bit fancier. I want you to change the program so that it does the following:

It asks the user to provide the number of students in the class.


Then, the program repeats the following for each student in the class. First, it asks for the name of
the student and then it asks for the number of programs they have written.
The program stores the names of the students and the number of programs they have written in
two lists. It iterates over the values in these lists, it finds the student with the largest number of
programs and prints it in the terminal.

All in all, the output of the program should look like the following:

$ python3 first_program.py
Give me the number of students in the class: 3
Give me the name of the next student: Dimos
Give me the number of programs written by this student: 3
Give me the name of the next student: Maria
Give me the number of programs written by this student: 2
Give me the name of the next student: Helen
Give me the number of programs written by this student: 6
The best student is: Helen

I'll give you some time to try and write this program yourself. While doing this, you might realise you
need to do something that was not mentioned here yet. Don't worry, because this will happen to you
very frequently when programming. Before giving up, try to find out the information you need on
your own. Internet is your friend.

Done? Ok, here's the solution:

number_of_students_as_string = input("Give me the number of students in the


class: ")
number_of_students = int(number_of_students_as_string)

all_names = []
all_programs = []

for student_number in range(number_of_students):


student_name = input("Give me the name of the next student: ")
all_names.append(student_name)
student_programs_as_string = input("Give me the number of programs written
by this student: ")
student_programs = int(student_programs_as_string)
all_programs.append(student_programs)
best_student = all_names[0]
best_effort = all_programs[0]

for student_number in range(number_of_students):


if all_programs[student_number] > best_effort:
best_student = all_names[student_number]
best_effort = all_programs[student_number]

print("The best student is: " + best_student)

Of course, this is just one of the possible solutions. If your program has the same behaviour when you
run it, it's OK if the code is slightly different.

Looking back at the code, you might notice something slightly disappointing. Each student is
associated with a name and the number of programs they have written. However, each piece of
information lives separately in two different places. The names of the students live in the all_names
list and the number of programs written by them live in the all_programs list. This means if we want
to find the number of programs written by a specific student, we have to find the location in the first
list and search for the value of the same location in the second list. Wouldn't it be nice if we could have
all the information for each student in the same place? You are now going to learn one way to do this,
classes.

A class is a specific category of objects that share some common characteristics. In our previous
example, all students have a name and a number of programs they have written. The concept of the
class allows you to define these common characteristics that all the objects of this class have. Then,
everytime you need such an object you can create an instance of this class. I will now show you how
you can define a class for students:

class Student:
def __init__(self, student_name, number_of_programs):
self.name = student_name
self.programs = number_of_programs

This creates a class called Student. Every object of this class has two attributes, called name and
programs. __init__ is a special type of function, called a method . A method is a function that is
associated with a specific class. This method can only be used on a variable that has an object of this
class and this is done by typing the name of the variable, followed by a dot (.), the method name and
the input data. You might haven't noticed previously, but you have already used a method, append().
append() is a method that is associated with lists. For example, if you want to add the value George
to a list variable called student_names, you need to write student_names.append("George"). This
__init__ is a very special method, called constructor, that is used to create new instances of a class. It
receives as input the instance of the class that will be created (self) and a set of variables we specify
(e.g. name, number_of_programs). Inside the constructor, we can assign values to the attributes of a
class using the data passed into the __init__ method. In order to do this, we need to specify the
instance followed by a dot (.), the name of the attribute, the assignment operator (=) and the value we
need to assign. This is what self.name = student_name does - it creates a new attribute called name
and it assigns the data that is inside the input variable student_name. When you want to create an
instance of the class, you can just write the name of the class and the data passed into the constructor
enclosed in parentheses. Attention: you only need to specify values for the variables after self in the
__init__ method, since self is calculated automatically by the computer. Back in our example, if you
wanted to create an instance of a student with the name George that has written 3 programs, you
could do this with the following piece of code:

some_student = Student("George", 3)

Visually, you could think of this object in the following way.

You can also access the values of a specific attribute of an object. For instance, if you wanted to access
the field name of the student object we just created, you could do this by writing some_student.name,
which would return the value George.

Let's go back to our last program now and see if we can improve it. If we defined a student class as I
just showed you, we could have a single list that stores all the student objects, instead of two separate
lists. In order to find the best student, we could just iterate over all the objects in this list. If you follow
this advice and change the code, the program will look something like the following:

class Student:
def __init__(self, student_name, number_of_programs):
self.name = student_name
self.programs = number_of_programs

number_of_students_as_string = input("Give me the number of students in the


class: ")
number_of_students = int(number_of_students_as_string)

all_students = []

for student_number in range(number_of_students):


student_name = input("Give me the name of the next student: ")
student_programs_as_string = input("Give me the number of programs written
by this student: ")
student_programs = int(student_programs_as_string)
student = Student(student_name, student_programs)
all_students.append(student)

best_student = all_students[0]
for student in all_students:
if student.programs > best_student.programs:
best_student = all_students[student_number]

print("The best student is: " + best_student.name)

Feel free to study the code to understand exactly what it does. You can also compare with the previous
program and see if you like this one better. They both achieve the same purpose, but in a different way.
How the code looks like might seem irrelevant to you now as long as it performs the task you need.
However, it is very important as you start working on the same code with other people, since they also
need to understand your code and also change it in order to improve the program.

Before completing this chapter, I want to show you one more data structure that can be very useful,
dictionaries. A dictionary contains pairs of data, known as "key - value" pairs. The first item of the pair
is the key and the second is the value for this key. Dictionaries make it easy to store data in an
organised way and search easily for the value of a specific key. For example, we could use a dictionary
to store the students of a class organised by their names. This would make it easier to find a student
without having to search a full list. Here is some code showing how to create an empty dictionary, add
students by their names and then search a student by their name:

students_dictionary = {}
students_dictionary["Maria"] = Student("Maria", 5)
students_dictionary["Helen"] = Student("Helen", 6)

helen = students_dictionary["Helen"]
helen_programs = helen.programs
print("Helen has written " + str(helen_programs) + " programs." )

If that helps, you can think of dictionaries as a chest of drawers, where each drawer contains a label
with the key outside and the value inside it.
Let's recap
First of all, congratulations for making it this far! Let's review what you have learned so far:

We can use programming languages in order to give instructions to a computer, so that it can
perform the tasks we need.
In a program, we can have simple data types, such as numbers or strings, but we can also have
more complicated data structures, such as lists and dictionaries. We can also create our own types,
by defining classes.
A programming language provides us with structures we can use to do complicated tasks. For
example, we can use if statements to perform different tasks depending on whether something is
true or not. We can also use for loops to execute the same task many times.

Challenge
Here is an extra challenge, in case you want to try something harder. Build a program that will do the
following:

It will receive the number of students in a class. If the size of the class is smaller than 1, the program
will do nothing. Otherwise, the program will follow the steps below.
It will ask the user to provide some data for every student of the class. This data will contain the
name of the student and their grade in three different classes: English, Geography and Mathematics.
The grade will be a number from 0 to 10.
The program will calculate the sum of all grades for each student and will find the students with
the highest and lowest sum. It will print these students on the terminal.

To give you an idea, when you run the program it will look like the following:

$ python3 first_program.py
Give me the number of students in the class: 3
Give me the name of the next student: Alex
Give me the grade this student got in English: 6
Give me the grade this student got in Geography: 7
Give me the grade this student got in Mathematics: 5
Give me the name of the next student: Maria
Give me the grade this student got in English: 6
Give me the grade this student got in Geography: 4
Give me the grade this student got in Mathematics: 8
Give me the name of the next student: Helen
Give me the grade this student got in English: 9
Give me the grade this student got in Geography: 8
Give me the grade this student got in Mathematics: 9
The student with the highest grade is: Helen
The student with the lowest grade is: Alex
Looking inside a computer

In the previous chapter, you learned how to program a computer. Even though you managed to make
your computer do things for you, it was still unclear how the computer was able to do these things. In
this chapter, you will learn more about how the computer works.

Computers generally contain 3 main parts that help them perform tasks:

a central processing unit, also called CPU or processor. This is the part of the computer that
performs calculations, such as calculating 40+2 or programs == 0. You can think of it as the brain
of a computer.
the memory. This is the part that remembers all the data created and accessed by the processor to
perform any necessary tasks. For instance, this is where the data for variables are stored. There are
many different types of memory, but when we say memory in this book we will mean volatile
memory. This is memory that requires power to keep the information stored inside. As a result,
once the computer is shut down any data stored in memory is lost.
the disk. This is a device that can keep information stored in it regardless of whether the computer
is on or off. As a result, the computer can store on a disk the data that need to be stored
permanently. For instance, if you wanted to use a computer to keep track of the students in a class
and their grades, you would probably store that data on a disk so that you wouldn't lose them if the
computer was shut down or crashed.
In reality, the internal structure of a computer is much more complicated, but I tried to keep things as
simple as possible here to avoid confusing you.

You have already seen how to make use of memory. Every time you stored some value in a variable or
accessed the value stored in a variable in your previous programs, you were essentially making use of
the computer's memory. However, we haven't used the disk yet. Now is a good time to give this a try
too. The simplest way to store data in a disk is using files. A file is a container of data stored in a disk.
If you have used a computer before, you must be familiar with files already. When you store a photo in
your computer, you are creating a file that contains the data of the photo.

Before writing our new program, let's first learn how to write and read data from files in Python.

Before accessing a file, you first need to open it. You can do this using the open function as shown
below. The first variable passed in open is the name of the file and the second one is a text
indicating how the file will be used. This can be "r" (as in the example) if you want to read the file,
"w" if you want to write on top of the data that already exists in the file and "a" if you want to keep
the existing data in the file and write new data at the end of the file. The function returns an object
representing the file that has now been opened.

file = open(<name_of_the_file>, "r")

You can use this file object to read data or write data to the file. There are many different methods
you can use for this. We will focus here on two simple methods: read and write. The read method
receives no input and returns the contents of the file as text. If the file contains many different lines,
you might want to split them and process them separately. You can do this using a method called
splitlines(), as shown in the example below. The write method receives a single text variable as
input and writes this text into the file. You might want to add data in separate lines in a text file.
There is a special text that represents a new line, "\n". So, you can just write this text into the file
everytime you want to add a new line. The example below shows how you can read all the data
from a file and split the lines apart. It also shows you can write data in a file in separate lines.
file = open("file.txt", "r")
file_contents = file.read()
lines = file_contents.splitlines()
print("The first line is: " + lines[0])

file = open("file.txt", "w")


file.write("First line")
file.write("\n")
file.write("Second line")
file.write("\n")

Now you know everything you need for your next program, a directory of the students in a class! The
program you will create will do the following:

First it will ask the user whether it wants to see the students that are currently enrolled in the class
or enroll new students. The user will need to type a specific text for each option.
If the user wants to add new students, the program will ask the user to provide the name of the new
student. It will then open a file named "students.txt" and write the name of the student in the file.
The names of the students will need to be separated into separate lines, so we can read them
properly, when needed.
If the user wants to see the current students, the program will open the "students.txt" file, read the
name of each student and print them in the console.
If the text provided by the user is gibberish, the program will print a message to let the user know
that this option is not valid and will then terminate.

All in all, when you run the program you should get something like the following:

$ python first_program.py
Do you want to see the current students (type see) or add new students (type
add): add
Please provide the name of the new student: George
Student added!
$ python first_program.py
Do you want to see the current students (type see) or add new students (type
add): add
Please provide the name of the new student: Maria
Student added!
$ python first_program.py
Do you want to see the current students (type see) or add new students (type
add): add
Please provide the name of the new student: Alex
Student added!
$ python first_program.py
Do you want to see the current students (type see) or add new students (type
add): see
The students in the class are:
George
Maria
Alex
I will pause now as usual and give you some time to try and write this program.

....

When you are ready, you can have a look at a solution below:

choice = input("Do you want to see the current students (type see) or add new
students (type add): ")
filename = "students.txt"

if choice == "see":
file = open(filename, "r")
file_contents = file.read()
student_names = file_contents.splitlines()
print("The students in the class are:")
for name in student_names:
print(name)
elif choice == "add":
file = open(filename, "a")
student_name = input("Please provide the name of the new student: ")
file.write(student_name)
file.write("\n")
print("Student added!")
else:
print(choice + " is an invalid choice")
print("The only valid options are: see and add.")

At this point, you might be thinking that computers can be really smart. In fact, computers are quite
dumb! They can only do whatever we instruct them to do. However, using simple instructions we can
give computers very complicated programs that can do very interesting things. Computers can also do
some things much more quickly than humans could.

When we - humans - communicate with each other, we rely on a basic set of symbols. For example,
when talking with each other, we rely on a set of characters, our alphabet. Using these characters, we
can build words and then we can combine words to create sentences. In a similar way, when doing
mathematical calculations, we rely on a set of digits, starting from 0 and going up to 9. Using these
digits, we can build large numbers and perform calculations with them.

In contrast to humans, computers cannot understand these characters or numbers. They can only
understand two basic symbols: 0 and 1. The main reason is computers do not have ears or eyes to read
or hear them. Instead, they are physical devices based on electricity. As a result, these two symbols (0
and 1) can be represented by whether there is an electrical signal or not, similar to how other electrical
devices like light bulbs work. You could imagine a light bulb being turned off representing the value 0
and a light bulb that is turned on representing the value 1.
I know what you might be thinking now. That can't be true as you just wrote programs that were using
proper characters (e.g. if, for, print) and digits (e.g. 42), so the computer must have been somehow
able to make sense of them! If you were thinking that, you are absolutely right. In order for the
computer to be able to understand things more complicated than a simple 0 or 1, the computer makes
use of a complicated language, which was not visible to you yet. This language allows the computer to
combine multiple 0s and 1s to create words and numbers. It is as if the computer is able to line up
many light bulbs in a row! We can now have a look at how the computer is able to translate all this 0s
and 1s into words and numbers. Of course, there is a very smart system behind this!

Let's start with numbers first. When translating a sequence of 0s and 1s into a number, the computer
starts reading from the right and adds each symbol. However, each symbol is multiplied to a number
before being added. The first one is multiplied by 1, the second one is multiplied by 2, the third one is
multiplied by 2 * 2 = 4, the fourth one is multiplied by 2 * 2 * 2 = 8 etc. Hopefully, you can see the logic
behind this. Let's look at a few examples and see how this translation works in practice:

The sequence 010 is translated into (0 * 4) + (1 * 2) + (0 * 1) = 2.


The sequence 111 is translated into (1 * 4) + (1 * 2) + (1 * 1) = 7.
The sequence 101 is translated into (1 * 4) + (0 * 2) + (1 * 1) = 5.

Ok, let's have a look at characters now, which is a bit different to the system behind numbers. Even
though there can be many different numbers, there is only a specific number of characters the
computer needs to understand. For example, the English alphabet has only 26 letters. As a result, the
computer can select a specific combination of 0s and 1s for each one of those characters, instead of
using complex mathematical formulas as with numbers. For instance, the computer could use a table
to translate from and to letters, as shown below.

Letters 0s and 1s

a 000001

b 000010

c 000011

d 000100

.... ...
The computer could then use this table to translate a word that is made out of multiple letters. For
example:

The word "bad" would be translated into 000010 000001 000100.


The word "dad" would be translated into 000100 000001 000100.

This system based on 0s and 1s is called the binary system. Each 0 or 1 is called a bit. A group of 8 bits
is called a byte.

The reason you haven't seen all these 0s and 1s so far is because people have built a lot of programs to
make it easier to work with computers without having to deal with them. For example, the programs
you wrote in Python before were being translated into the language the computer speaks without you
knowing about it. In the same way, the files stored in the computer (such as photos or the
students.txt file you created in your previous program) are stored in 0s and 1s, but the programs
we use translate these into something that is easier to understand for humans. In the case of photos, 0s
and 1s are translated into a picture. In case of a text file (such as students.txt or our program), the 0s
and 1s are translated into characters we can read.

If you are curious enough, you can actually see this with your own eyes:

If you open the "students.txt" file with Sublime, you will see that Sublime translates all the bits into
characters that you can read. However, there are programs that you can use to see the actual bits
stored in the file. One of those is called xxd. You can use this program from the terminal to see the
contents of the "students.txt" file in binary form with the following command:

xxd -b students.txt

Unfortunately, this command is not available on Windows, but Appendix A explains how you can
achieve this in a different way.
This will print something like the following:
00000000: 01000111 01100101 01101111 01110010 01100111 01100101 George
00000006: 00001010 01001101 01100001 01110010 01101001 01100001 .Maria
0000000c: 00001010 01000001 01101100 01100101 01111000 00001010 .Alex.
The data in the middle column (shown in bold above) is the binary contents of the file.

Of course, you can do the same for text files or photos, if you want.

Let's recap
In this chapter, you learned:

what are the basic components of a computer: a processor, the memory and the disk. You have also
learned the main difference between memory and disk. Even though both can be used by the
computer to store data, memory is used to store data temporarily only as long as the computer is
running, while disk is used to store data permanently that can survive restarts of the computer.
how to use the disk of a computer to write data permanently.
what is the binary system and how computers use it to represent data, such as numbers or
characters.
why and how we can interact with a computer without having to use the binary system.
How computers talk to each other

The previous chapter explained what is inside a computer, how we can use each of those parts, what is
the actual language a computer can "speak" and understand (the binary system), and how we can talk
to a computer without necessarily having to know how to "speak" this language. In this chapter, we
will learn how computers can talk to each other.

As explained before, computers speak in 0s and 1s. This is also the language they use to speak to each
other. As a result, we need a way to send these 0s and 1s from one computer to another. In reality,
there are many different ways to do this, but we will only look at one to keep things simple. I
mentioned before that 0s and 1s can be represented in the form of electricity. The same concept can be
used to send to send 0s and 1s from one computer to another. Computers can send electrical signals to
each other to represent 0s and 1s. The picture below shows how this works.

Computers contain an extra piece that was not mentioned in the previous chapter. This piece is the
network interface card or controller (NIC) and is responsible for sending data through the network to
a separate computer connected to the network. This works in the following way:

A computer program needs to send data to a program running in a different computer. The
processor sends that data (the sequence of 0s and 1s) to the network interface card along with the
address of the computer where the data needs to be sent.
The network interface card is then responsible for converting these 0s and 1s into a physical form.
In the photo above, 0s and 1s are converted into an electrical signal that is sent into a wire.
The network interface card on the other side of the wire is listening for electrical signals and
converts them into 0s and 1s.
The data are then given by the network interface card to the processor, so that they can be given to
the program that is waiting for them.

In this way, computers are able to send "words" to each other - remember words can be created from
letters, which can be represented using 0s and 1s. However, the programs need a way to put some
structure in these words, so that they make sense and the two programs can understand each other,
instead of speaking on top of each other or talking nonsense. This is not something that is only needed
in computers by the way, you can also see that happening with humans. Every time we call someone,
the first sentences are typically the same in order to give a structure to our conversation. The person
answering the phone typically opens the conversation with a simple "Hi" or "Hello" and the person
that called greets back, informs the other person who they are and starts talking about the subject they
wanted to talk about. This set of rules that determine who is supposed to talk and what they are
supposed to say is known as a communication protocol.

We will now have a look at one of these protocols used by computer programs to get a better idea of
what it looks like. This protocol is called the Hypertext Transfer Protocol (HTTP) and you might have
already used it even though you might not have realised it yet. Every time you visit a website (e.g.
when you do a Google search), you are making use of the HTTP protocol. This means a program on
your computer is sending data to a program on another computer and this data are structured in a
specific way, as specified by the HTTP protocol. In this way, the two programs on each side can easily
understand what they are talking about. The program on your side is called a web browser. It is
responsible for asking the program on the other side for a specific page and show the contents of this
page to you in a nice way. Some examples of web browsers you might be familiar with are Chrome,
Firefox or Safari. The program on the other side is called a web server. It is responsible for receiving
requests from browsers (or other similar programs) and providing them with the contents of the
requested pages.

Let's have a look at the structure of HTTP messages. There are two main categories of HTTP messages:

request messages, which are sent by the browser program to the server program.
response messages, which are sent by the server program back to the browser program.
A request message consists of the following parts, which are placed in separate lines:

a line of text, also known as request line. This contains a word that indicates the desired action to
be performed. There is only a specific set words that can be used as actions. An example is "GET",
which is used to get the contents of a specific page. This is is followed by another word that
indicates the location of the page that is requested (e.g. /images/flower.jpg to get the file of a
flower image) and the version of the HTTP protocol that is used. An example request line would
look like this: GET /images/flower.jpg HTTP/1.1.
a list of additional attributes, known as headers. These can be used to provide additional
information to the server. Each header is placed in a separate line and consists of two main parts:
the name of the header and the corresponding value. For example, Host: google.com would
correspond to the header with name Host and value google.com.
an empty line
an optional body that contains additional data. In case of "GET" requests, this is not needed and is
left empty. However, this can be used in different type of requests. For example, when a browser is
submitting data to the server (e.g. when you upload a new photo in your Facebook account), then
the body can contain this data.

A response message consists of the following parts (again placed in separate lines):

a line of text, also known as status line. This contains the version of the protocol that is used, a
number representing the status of the response and a sentence with some more details on the
reasons behind the status. In case of a successful response, the number is 200 and the message is
"OK".
a list of headers, similar to the request message.
an empty line.
an optional message body, which can contain different data depending on the type of request that
was made. If a web page was requested, this part contains the contents of the page.

You can actually see these requests and responses with your own eyes. If you want to do this, you can
follow these steps:

First, you will have to download a tool called Wireshark, which allows you to see data that are sent
and received by your computer. You can download this tool here: https://www.wireshark.org/#do
wnload
Once you download and open this tool, you can see a blue button at the top left of the window.
Once you press that, the tool will start capturing the traffic from and to your computer. We are
interested only for HTTP requests and responses, so you can type "http" in the bar below after
pressing that button. This will filter the traffic and show you only HTTP messages.
After you have pressed the button, you can open your browser, type "example.com/index.html" in
the bar and press Enter. This will just make your browser visit a very simple website, which looks
like this.
Now, you can go back to Wireshark and view the requests and responses exchanged between your
browser and the server of this website. It will look like the image below. In the middle panel, we can
see there are 4 entries, which means 4 messages were exchanged between our browser and the web
server, 2 requests and 2 responses. The first request is selected in the image below and the two main
parts of the HTTP request have been highlighted, the request line and the headers. As you can see
your browser made a "GET" request for the page "index.html" and the server returned a response
with "200 OK" status. If you click on the second entry, you can see the contents of the response in
the same way.

Now that you have seen how computer programs can use the HTTP protocol to communicate with
each other, you are ready to create a program that makes use of HTTP! You are going to create your
first server program and interact with it through your browser. But before you start, let's look at some
basic building blocks Python provides to do this.

The following is a very basic server program in Python:


from http.server import BaseHTTPRequestHandler,HTTPServer

class MyHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-type','text/plain')
self.end_headers()
self.wfile.write("Hello world!".encode())

server = HTTPServer(("", 8000), MyHandler)

print("Server has started!")


server.serve_forever()

Let's go through the various parts and explain what they do:

You must have noticed an import command at the top of the program, which we hadn't used yet.
Python provides a lot of code that you can use to do more complicated things. These groupings of
code are usually called libraries. The import command allows a program to use code from these
libraries. Here specifically we are using two classes (BaseHTTPRequestHandler and HTTPServer)
from the HTTP library.
Right after that, you can see something that is also new: class
MyHandler(BaseHttpRequestHandler). Let's unpack this slowly to understand what it means. In
Python, classes can "inherit" from other classes. This means that one class can copy all the attributes
& methods of another class. This is what this piece of code does: it defines a class MyHandler that
inherits from another class BaseHTTPRequestHandler. This is the class that will handle HTTP
requests as they come in from a browser program. Everytime there is an HTTP request coming in,
this class will call a method with the name do_<request_type> that will handle the request. As a
result, we define a method do_GET and we know this method will be called everytime the server
receives an HTTP "GET" request.
The BaseHTTPRequestHandler also provides methods that we can use to perform basic operations,
such as specify the status code of an HTTP response or write the right header values and body. This
is what we actually do inside the do_GET method: we set the status code to 200 to show that the
request was handled successfully, we write a single header (Content-type) to show that the body
contains plain text (this is why the header has the value text/plain) and we write the text "Hello
world!" in the body. Note that we call the method encode() on the text in order to convert it into
bytes. The browser that will receive this response will read the Content-type header to understand
what kind of data it contains, so that it can show them to the user in the right way. In this case it's
just text, so the browser will print the text in the screen.
After we have defined our handler class, we start an HTTP server with the following command:
server = HTTPServer(("", 8000), MyHandler). This creates an instance of the HTTPServer
class using its constructor method. The first variable is the "address" the server program will be
listening on - we haven't talked a lot about what an address of a program means, so you can ignore
this for now. The second variable is the handler that the server will use to handle requests. In this
case, this is the class we just defined, MyHandler.
Then we just need to call the method serve_forever() and the server will start handling requests
until we stop the program.
After you've typed this in a text file (e.g. server.py), you can start the program server the same way
you did for the other programs so far (e.g. python3 server.py). All you have to do now to interact
with the server program is to open your web browser, type "localhost:8000" in the bar (this is the
address of the program btw) and press Enter. You should then see the following in your browser:

If you made it this far, well done!

You are now ready to make your program a bit more useful. I want you to improve the previous
program, so that it does the following:

If the request is for any other path other than "/students", then the server program will return a
status code of 404, which means there is no such page. Tip: you can access the location of the request
using the attribute self.path.
If the request is for the path "/students", then the server program will open the file we were using
in our initial program that contains the names of the students of the class (students.txt), it will
read the names of all the students and write them inside the response.

All in all, the responses from the program for requests for the location "/students" will look like the
following:

Ok, I will give you some time to try and write this program now.

Done? Great, here is one possible solution:

from http.server import BaseHTTPRequestHandler,HTTPServer

class MyHandler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == "/students":
self.send_response(200)
self.send_header('Content-type','text/plain')
self.end_headers()
filename = "students.txt"
file = open(filename, "r")
file_contents = file.read()
student_names = file_contents.splitlines()
response = "The students in the class are: "
for name in student_names:
response = response + name + " "
self.wfile.write(response.encode())
else:
self.send_response(404)
self.end_headers()

server = HTTPServer(("", 8000), MyHandler)

print("Server has started!")


server.serve_forever()

Let's recap
In this chapter, you learned:

how computers send data to each other.


why computer programs have to use communication protocols to exchange data in a useful way.
what is the HTTP protocol and how it works.
how to write a server program that can communicate with a browser using the HTTP protocol.

Challenge
Here is an extra challenge, in case you want to try something harder. Build a program that will do the
following:

Similar to our previous program, when asked for the "/students" location, it will return the name of
all the students in the class.
However, the browser program will also be able to add new students in the class. In order to do so,
it will have to make a request to the location "/add_student/<student_name>". For example, if the
browser requests the location "/add_student/Dimos", the server program will add the name
"Dimos" in the file that contains the student names.

Below are some tips in case you get stuck:

The string class in Python contains a lot of methods that can help you transform strings. For
example, the method startswith(beginning) checks if the beginning of the string contains the
text contained in the beginning variable. Another useful method is replace(text_to_replace,
new_text) that returns the original string, but after having removed the characters that match
those in the variable text_to_replace with those in the variable new_text.
Making our programs easier to
use

In the previous chapter, you wrote a server program that could communicate with a browser in order
to show the students enrolled in a class. On its own, this can be a very useful program. For example, a
teacher could use it to keep track of the students of her class. However, you might have noticed that
the program could be much better. As a user of the program, the teacher would have to know a lot of
details about the program in order to use it successfully. If you tried to complete the extra challenge,
you might have noticed that the user would need to know the exact location that needs to be used to
add a new student. If they didn't know this or they mistyped it (e.g. they used add_students/Dimos),
the program would not work. Apart from that, the program was not very user friendly as it could just
print some text in the console. However, most websites you visit nowadays are very nice to use. In this
chapter, you will learn how to make your programs prettier and easier to use.

Your previous server program was able to return simple text. Browsers are not only able to understand
plain text, they can also handle a lot of different forms of data. One of those forms is a language, called
Hypertext Markup Language (HTML). This is a language that can be used to create documents that
contain information, which can be displayed by a browser in a more structured way. Most websites
you visit are written in HTML. If you are curious enough, you can actually see that yourself. For
example, if you are using Chrome as a browser, you can visit a website, right-click and select the
option "View Page Source". This will open a new window that will contain the HTML content of this
page.
Your first exercise will be to create your first page in HTML. Before you start doing that though, you
will first need to learn the basics of HMTL.

The most basic piece in HTML is called a tag. A tag can be used to represent different types of
elements, such as lists, paragraphs, images etc. Tags typically come in pairs, an opening tag followed
by the content of the tag and a closing tag. The content of a tag can simply be text or it can be more
complicated HTML content. An opening tag consists of a left angle bracket (<), the name of the tag and
a right angle bracket (>), such as <tag>. A closing tag is similar, but the left angle bracket is followed
by a slash (/), such as </tag>. In some cases, an element cannot contain additional content, so there is
only an opening tag. The opening tag can also contain attributes within the tag, such as <tag
attribute1="value1" attribute2="value2">. Let's have a look at some of the most common tags:

The tag <html> represents the whole web page. It has a corresponding closing tag (</html>), so
everything that comes between those two describes the whole page.
The tag <head> contains metadata about the page, such as the title of the page. It has a
corresponding closing tag (</head>) and it should be placed between the <html> and </html>
tags.
The tag <body> contains the visible content of the page. It also has a corresponding closing tag
(</body>) and it should be placed between the <html> and </html> tags.
The tag <title> contains the title of the page. The title must be written as text between the
<title> and the </title> tag. This is part of the page's metadata, so it should be placed between
<head> and </head>.
The tag <p> represents a paragraph of text. This text is written between the <p> and the </p> tag.
The tag <div> represents a section that can contain other elements (you can think of it as a box). It
has a corresponding closing tag </div> and you can add many elements between them.
The tag <ul> represents an unordered list of items. It has a corresponding closing tag </ul> and all
the items need to be placed between these two. Each list item is represented by a <li> tag and the
contents of each item need to be put between a <li> and </li> tag.

Using these tags you can create a simple web page, like the following:

<html>
<head>
<title>Our first page</title>
</head>

<body>
<div>
<p>Welcome to our first page</p>
</div>
<div>
<p>Here is what we have learned so far:</p>
<ul>
<li>How to write a program</li>
<li>What is inside a computer</li>
<li>How computers talk to each other</li>
</ul>
</div>
</body>
</html>
At this point, you can take some time and study the above to understand the structure of the tags and
what this page would be like when displayed in a browser. After you have done this, you can create a
file in your computer, add the text above in the file and save it with an .html extension, e.g.
first_page.html. Then, you can right-click on the file, select the "Open with" option and select your
browser. The browser will then read this file and display the page, which should look like the
following:

This is quite cool, but probably not very useful in real life. But, do you know what would be cooler? If
we could serve HTML pages from our server program, instead of just serving plain text! If you are
thinking that is hard, you might be surprised by how easy it is to achieve based on what you have
learned so far. I will give you a few tips and see if you can figure it out yourself. Let's say we want to
improve our previous program, so that it can show the list of students in the class in a well-structured
HTML page, which would look like the following.

Here are some tips (just in case you need them):

You can reuse your previous server program. Make sure you use the right location in your browser
and you configure your handler properly.
You can create the content of the web page as a big text by combining different smaller texts as you
go. However, you might notice your program becomes very hard to read. If you want to make it a
bit easier to work with, you can create a semi-complete version of your page and save it in a file, as
we did previously. Then you can read the contents of the file from the server program as text. In this
case, you might have to add additional content, such as the names of the students. One way to do
this is by adding some "dummy" tag in your page (such as <students_list_placeholder>),
which is later replaced by the final content. As a reminder, you can replace data in a string using
the method .replace(text_to_be_replaced, replacement_text).
In order to make sure your browser will understand the data returned by the server is not plain
text, but HTML, you will also have to change the value of the Content-type header to text/html.

You can now start working on your solution, I'll wait here until you're done.

Ready? Great! Here's my solution in case you want to have a look at it.

First of all, I created a file containing the semi-complete version of my page. I called it
class_page.html and I wrote the following inside:

<html>
<head>
<title>Computer Science for kids - page</title>
</head>

<body>
<div>
<p>Welcome to the class of "Computer Science for kids"</p>
</div>
<div>
<p>The students enrolled in this class are:</p>
<students_list_placeholder>
</div>
</body>
</html>

Next, I adjusted the server program, so that it read this page and replaced the placeholder tag with
the list of student names. The code to do that is the following:

from http.server import BaseHTTPRequestHandler,HTTPServer

page_file = open("class_page.html", "r")


class_page = page_file.read()

class MyHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-type','text/html')
self.end_headers()
filename = "students.txt"
file = open(filename, "r")
file_contents = file.read()
student_names = file_contents.splitlines()
students_list = "<ul>"
for name in student_names:
student_item = "<li>" + name + "</li>"
students_list = students_list + student_item
students_list = students_list + "</ul>"
class_page_final = class_page.replace("
<students_list_placeholder>", students_list)
self.wfile.write(class_page_final.encode())

server = HTTPServer(("", 8000), MyHandler)


print("Server has started!")
server.serve_forever()

As you might have realised by now, HTML looks a lot better than plain text and the page is easier to
read, since we can add structure to our text, such as the list of student names. However, the page still
looks very simple stylistically, so we will now work on making it more fancy. To do this, we will use a
language, called Cascading Style Sheets (CSS). CSS is a language we can use to specify how the
various elements in an HTML document should be presented. So, HTML helps us give structure to a
page and CSS allows us to give style.

Let's look at the structure of CSS code first. CSS code contains a list of rules. Each rule contains one or
more selectors and a declaration block. The selector specifies which HTML elements should be styled
based on this rule and the declaration block specifies what kind of styling will be added to the selected
elements. There are many ways to define a selector, but we will look at the 3 most basic ways here:

the simplest way is to select all the elements of a specific category. For example, if we want to style
all the paragraphs in a specific way, we can write a rule that selects all of them in the following way:

p {
...
}

sometimes, we'll need to select a specific element in the document. In order to do this, we can use a
combination of HTML and CSS code. HTML provides the attribute id that we can use to give each
element a single name. Then, we can select the element with a specified id in CSS by using the
character # followed by the identifier name. The code below shows how we could assign an
identifier to a paragraph and then select this paragraph from CSS to give some styling:

<p id="our_paragraph">This is some text inside the paragraph.</p>

#our_paragraph {
....
}

in some cases, we want to select a group of elements (instead of just one). We can do this again
using a combination of HTML and CSS code. HTML provides the attribute class that we can use to
assign an element to a group (called a class in this case) with a specific name. Then, we can select all
the elements in a group by using a dot (.) followed by the name of the group/class. The code below
shows how we could create assign two different elements in a class called "red" and then select
these elements from CSS:

<p class="red">This some text that needs to be red.</p>


<p>This is some normal text.</p>
<p class="red">This is some more text that needs to be red.</p>

.red {
...
}
The selector is followed by the declaration block, which contains a list of declarations in curly brackets.
Each declaration consists of a property, a colon (:), the value for this property and a semi-colon (;) that
separates it from the next declaration. There are many different properties in the CSS language, so we
will not be able to cover everything here - it's quite easy to search for properties on the Internet, if you
are interested. Here are some of the basic properties:

color sets the colour of the text. The value is some text that can take different values, such as
yellow, green, black etc.
background-color sets the background colour of an element. It can take similar values as color.
font-family sets the font family of text. The value is some text that can take different values, such
as monospace, serif, sans-serif etc.

As I mentioned before, you can think of each element in HTML as a box. CSS allows you to change
various characteristics of this box, such as the space between the content of the box and its borders, the
width of the borders or the space between this box and the surrounding boxes. In CSS, this is known
as the box model and it comes with many properties that you can use to change how this "box" will
look like. The diagram below summarises the role of each property.

In the center of the box, we have the content of the box. This can be just text or it can have other
elements, such as an image.
The box can have a line that shows where the boundaries of the box are. This is known as the
border and there are many properties that can be used to change how the border looks like. For
example, the border-style property changes the style of the border line (solid, dotted etc.), the
border-width property changes the width of this line and the border-color changes the color of
the border line.
The space between the content of the box and its border is called padding and you can specify how
big that should be using the padding property.
The space between the border of a box and the surrounding boxes is called margin and you can
specify how big that should be using the margin property.

All of this CSS code should be included in the head element of an HTML page inside a style tag. For
example, if you wanted to change the background of a page to be black you could do this by adding
the following in the HTML page:

...
<head>
<style>
body {
background-color: black;
}
</style>
</head>
...

You now know pretty much all you need to start making your pages prettier using CSS. As I
explained, there are many different CSS properties you can use, so it is up to you how much time you
want to spend. Remember: Internet is your best friend when trying to find out how to do something I
haven't explained here!

I want you now to try your best to improve the page using CSS, so that it looks like the one below.

Here's the solution in case you want to have a look when you're done.

Below is the content of the class_page.html file:

<html>
<head>
<title>Computer Science for kids - page</title>
<style>
body {
background-color: grey;
}
#welcome_box {
background-color: palegoldenrod;
font-family: fantasy;
padding: 10px;
margin: 20px;
border-style: outset;
}
#board {
background-color: darkgreen;
color: white;
font-family: cursive;
padding: 15px;
border-color: saddlebrown;
border-style: ridge;
border-width: medium;
box-shadow: 16px 6px 8px 4px black;
}
</style>
</head>

<body>
<div id="welcome_box">
<p>Welcome to the class of "Computer Science for kids"</p>
</div>
<div id="board">
<p>The students enrolled in this class are:</p>
<students_list_placeholder>
</div>
</body>
</html>

The server program code doesn't need to change.

Let's recap
In this chapter, you learned:

how you can use HTML to add structure to the content of your page.
how to generate HTML for a program, so that you can fill the content of the page dynamically
depending on some data.
how you can use CSS to add style to your page.

Challenge
Here is an extra challenge, in case you want to try something even harder. Warning: this is a quite hard
challenge, so don't get disappointed if you are not able to complete it on your own. Improve the server
program written in the beginning of this chapter so that:
Below the list of student names, the page also contains a form that the user can fill to enroll a new
student.
When a user fills the form with a new student name and submits it, the program adds a new
student in the text file. The page is also refreshed to show the updated list of students.

The page should look like the following:

Below are some tips in case you get stuck:

The <form> tag can be used to create a form and you can include many different tags inside it. For
example, the <input> tag with an attribute type="text" can be used for a text entry, while with an
attribute type="submit" can be used for a button that submits the form.
Unless you specify otherwise, by default the form will submit the data as part of the request's body.
The data will be formatted in the form attribute_name=value, where attribute_name will be
the name of the specific entry in the form and value will be the value the user provided.
In order to read the body of a "POST" request in a server program, you can call the
read(number_of_characters) method on the self.rfile variable and convert the result from
bytes to a string (you can do this via the decode() method). However, you need to specify the exact
number of bytes you want to read from the body. Fortunately, this is specified by the browser in the
header Content-Length.
The data you will read from the body will just be a string (e.g. name=Dimos), so you will need to
parse this. There is a Python library that contains a method that can help you do that. You will need
to import the library with the following command from urllib.parse import parse_qs. Then,
you can use the method parse_qs(string_to_parse) to parse this string. This will return a
dictionary that maps the name of each entry in the form to a list that contains the values specified
by the user (in our case, this list has only one item).
Amongst the many different status codes, there is one that instructs the browser to redirect to a
specific page. You can use it to force the browser to reload the page and show the student that was
just added. The status code is 302 and you also need to populate the header Location with the
page the browser needs to redirect to (that can be just / if you want to redirect to the home page).
If you get stuck trying to identify why your program is not working as you expected, you will need
to examine what is happening and what is the problem. If you suspect the problem is on the
browser, you can see if the browser is sending any HTTP request at all when you submit the form
and what these requests look like. One way to do that is using Wireshark, but browsers usually
provide tools you can use for the same reason. For example, Chrome provides what is known as
"Developer Tools", which allow you to see all the requests made by your browser and the responses.
You can do this by clicking on the upper-right-hand corner of the browser window, select "More
Tools" and then select "Developer Tools". This will open a window with a "Network" tab that
contains all the requests. If something is going wrong in your server program, you can add print
statements in various places in the program to see the content of variables to understand what
exactly is happening as the program is running each instruction.
How does the Internet work

So far, you have seen how two computers can communicate with each other, but this was an
oversimplified explanation of the truth. In general, two computers that need to communicate might
not be directly connected to each other. Going back to our previous example, your computer is
requesting a web page from a computer run by Google, but your computer is not directly connected to
Google's servers. Then, how does all this work? This is what we will be looking at in this chapter.

Going back to our example, your computer is communicating with a Google's computer with the help
of many other intermediate computers. This looks like the following:

As you can see, your computer is not directly connected to Google's computers. Instead, it is connected
to a computer that is connected to another computer, which is then connected to a Google's computer.
Of course, in real life there are many more computers connected to each other, but you get the point!
These computers in the middle are responsible for receiving data from a computer and delivering it to
a different computer, which might be nearby or at the other end of the planet! The farther the
destination is, the more computers that data will need to travel through. These intermediate
computers are typically called routers, because they route data from your computer to the destination
computer and vice versa. These routers are being run by companies, known as Internet Service
Providers (ISPs). When you pay these companies for an Internet subscription, you essentially pay
them to run these routers and connect your computer to them, so that you can send and receive data
from other computers around the world. The company on the other side (e.g. Google) also has to pay
in order to connect their servers to these routers, so that they can send data to their users like you.
Here, we have seen what happens for your computer and another computer, say Google's server. In
real life, there are many more computers involved. There are many people like you that want to access
websites and other services from their computer. At the same time, there are many companies like
Google that want to send data to their users. You might already know some of them, because you use
them in your daily life, such as Youtube, Netflix, Amazon etc. As a result, this picture can get a lot
more complicated if we show all of them.

This big network of connected computers is called the Internet.

You are now probably left wondering: with so many computers connected to each other, how do they
all know where to send each piece of data? This is all done through a combination of smart and
sophisticated protocols. We will look at some of them to help you understand how all this works in
more detail. Before we start looking at these protocols though, I want you to pause for a moment, look
at the previous picture and think whether this looks similar to some other way we humans use to
communicate with each other. It looks a bit like our postal system, right? When you send a letter to a
friend, this letter needs to go through many, different post offices until it reaches the mail box of your
friend. This is similar to how data needs to travel through multiple routers on the Internet. It is useful
to keep this analogy in mind as we go through the various protocols, since you will notice there are
many similarities that can help you understand the role of each protocol better.

As I mentioned many times so far, data travels from one computer, known as source, to another one,
known as destination. This means computers need to have some form of address. These addresses
will be useful in many ways: the intermediate routers can use the address of the destination computer
in order to determine which direction the data needs to go, while the address of the source computer is
useful to the destination computer, so that it knows who sent the data and where they should send any
response back to. If you think about the previous example of the postal system, addresses play very
similar roles. In that case, addresses consist of the country, the city, the street number etc. Computers
have a different form of address, known as an IP address. This address is just a number that's made of
32 bits, but it is typically written and displayed in a different format. This is done in the following
way: this number is cut into 4 pieces, where each one of them has 32 / 4 = 8 bits. Each one of those 8-
bit pieces is then converted into a number using the system I described in the previous chapters and
they are all displayed one after the other separated by a dot (.). To help you get an idea of how this
works, below is an example of this conversion for the IP address 216.58.198.174.

11011000001110101100011010101110 ↔ 11011000.00111010.11000110.10101110 ↔
216.58.198.174

Each computer can have one or more IP addresses. Looking at our picture above, each one of the
personal computers has a single IP address, but every router has multiple IP addresses, one for each
connection to a different computer. If we were to draw that, it would look something like this.

I hope I didn't scare you too much yet, because we still have a lot to talk about! As you might have
guessed, only knowing the IP address of the directly connected computers is not always enough for a
computer to be able to send data in the right direction. For example, let's assume the user at the top
(172.16.0.2) tries to send some data to Google's server (216.58.198.174). The user's computer is
only connected to one computer - the router with IP 172.16.0.1, so it naturally sends the data there.
Now, if this router only knew the IP addresses of the neighbour routers, it wouldn't be able to know in
which direction the data should be sent, right? In order to solve this problem, computers exchange
information with each other about the computers they are connected to. In this way, a computer can
have a picture of the overall network, not only its direct neighbours. The computer keeps this
information in a table. This table is called a routing table, because it contains information that is used
by the computer to route data to other computers in the network. This table contains a list of rows,
where each row corresponds to a destination IP address and also contains the IP address of the next
computer the data should be sent to in order to reach the final destination. This next computer is
known as the gateway. The picture below shows how this would work in our previous example.
Each computer would send a message to their neighbours telling them which computers they can send
messages to. Using this information every computer, would build its routing table. When the
computer received a message, it would use the routing table to find an entry with a destination IP
matching the one in the message, which would also contain the next computer the message should be
sent to. In the picture above, we see the routing table contains two entries: one that corresponds to
Google's server (216.58.198.174) and one corresponding to Netflix's server (54.154.83.5). Each
one of those entries contains the right gateway. In order to send a message to Netflix's server, the
router would have to forward the message to the router on its right (2.120.9.88). In order to send a
message to Google's server, it would have to forward the message to the router at the bottom
(99.83.70.62). The same thing would be repeated at every router as the message travels from the
original source to its destination. Take some time to make sure you understand how all this works in
the picture above, as it is quite complicated. I will now let you in on a little secret: the description of
the routing table I gave you is not 100% accurate and there are also other columns we haven't
discussed. I did that on purpose to make things a little simpler for you to understand. If you are
curious enough, you can always find out the full truth yourself (you can check Wikipedia, for
example).

Ok, so IP addresses are useful for computers so that they know which computers a message needs to
travel through in order to be delivered from a source computer A to a destination computer B.
However, they are quite hard to remember for us humans. I bet you probably had a hard time even
reading them in the previous pictures. As a result, there is another form of address that is easier to use
by humans, known as a domain name. This is made of simple characters separated by dots (.) and it
is what you usually type in your browser when you want to visit a website. For example, google.com
is a domain name. As a result, humans can use domain names to specify which computer they want to
send data to or receive data from and the computer translates these domains into IP addresses without
us having to do anything. They do this with the help of some other special computers, known as
domain name servers, which know the IP addresses that correspond to specific domain names. So,
when you want to visit a specific website in your browser, it works in the following way:

you type the domain name of the website you want to visit in your browser, say google.com.
your computer asks a domain name server what is the IP address for google.com. Let's say it's
216.58.198.174.
after learning this, your computer can send a message to the computer with IP address
216.58.198.174.
this message will then travel through the Internet, as we discussed previously.

So far, we have seen how a program in your computer (a browser) can communicate with a program
on another computer (a server program in Google's server). However, each computer can run many
different programs that might want to communicate with one or more programs on another computer.
If you think about the way you use your computer during the day, you might realise you are using
many programs at the same time. For examle, you might be using your browser to search something
on Google, while listening to some music at the same time or talking with friends on chat or through
video. In order for this to be possible, besides the destination IP address a message also needs a way to
indicate which program on this computer it is supposed to be delivered to. This is achieved through
the concept of a port, which can be a number from 0 up to 65535. Each program running on a
computer is given a port and other programs that need to communicate with it need to provide both
the IP address and the port of this program. The IP address will be used by the Internet computers to
deliver the message to the right computer and the port will be used by this computer to deliver the
message to the right program. The following picture shows how that would work if you tried to search
something on Google (using your browser), listen to some music (using an application like Spotify)
and talk with friends (using an application like Skype) all at the same time. Each message traveling
from your computer to another computer (such as Google's, Spotify's or Skype's servers) would
contain 4 main parts:

the IP address of your computer, known as source IP.


the port of the application on your computer, known as source port.
the IP address of the destination computer, known as destination IP.
the port of the application on the destination computer the message is destined to, known as the
destination port.

The various routers that are part of the Internet would use the destination IP address to route the
message properly, so that it arrives to its final destination computer. That computer will then use the
destination port to deliver the message to the right application. It will also use the source IP address
and source port to send any messages back to your computer. For example, Google will use those to
send you back the search results.

Time for a quick quiz now:

In the previous example, what will the values be for source IP address/port and destination IP
address/port when your computer requests a song from Spotify and what those values will be for the
message that is sent back from Spotify and contains the song's music?

! Ready? The answer is...

The message from your computer to Spotify's server will have the following values:
source IP: 172.16.0.2
source port: 1501
destination IP: 35.186.224.25
destination port: 80
The message from Spotify's server back to your computer will have the following values:
source IP: 35.186.224.25
source port: 80
destination IP: 172.16.0.2
destination port: 1501
You can now understand what's the meaning behind this ("", 8000) in the code we wrote to start
our server in the previous chapters:

server = HTTPServer(("", 8000), MyHandler)

This specified the IP address and the port for our program. The port was 8000 of course and the empty
text means the program was listening on all IP address of the computer. You might have noticed that
sometimes you don't specify a port when typing a website you want to visit and it still works. This is
because the default port for HTTP is 80. So, if you don't specify some port, your browser will send a
request to port 80.

You should now be able to understand what happens when your computer wants to send some data
to another computer and how this data travels through the Internet. However, you might ask yourself:
do all these computers in the Internet get to see the data they transfer? The answer is: it depends on
what communication protocol is used. Some protocols transfer data in their raw form, which means
that all these computers can see the data that are transferred through them. HTTP is one of those
protocols. Of course, there are cases where this would be unacceptable! Imagine if anyone could see
the password for your credit card everytime you tried to buy something on a website. For this reason,
there are protocols that change the data in a very clever way, so that they remain hidden to any other
computers except for the final destination computer who is able to read it. All the other computers see
garbled data that do not make any sense. HTTPS is a secure version of the HTTP protocol that does
this, so you can use websites safely.

This is done using two techniques, called encryption and decryption. Encryption takes some data and
converts them to another form that looks like random data. Decryption can take this random data and
convert them back to the original data. Both encryption and decryption make use of some extra data to
perform these conversions and this data should only be known by the programs that need to encrypt
or decrypt data. This data is known as an encryption or decryption key. Computers can use those two
techniques in the following way: program A encrypts the data to be sent and then sends the encrypted
data through the network. Program B receives the encrypted data, decrypts them and then can read
the original data. Any other computers in the Internet don't know the decryption key, which means
they cannot decrypt the data and all they see is random bits that don't make any sense.

Hopefully, this all makes sense and your question at this point is: how is this "magic" translation done
by encryption and decryption? The complete answer to this question can be very long and
complicated. So, I will try and you give a more simplified explanation here to help you understand
how this is done. Imagine you have a piece of text you want to send to another computer and you
want to encrypt it before sending it through the network. The simplest approach would be to replace
each character with another character. Of course, whoever wants to decrypt the data would also need
to know which character replaces which one. This information - which character replaces which one -
is essentially your encryption and decryption key. Let's look at how this would work with a simple
example.

As mentioned before, the encryption and decryption key would be the same. They would be a table,
which would contain one entry for each character that would also contain the character that would
replace it during encryption. Encryption would go over each one of the characters of the data, search
for an entry in the table that contained the character in the first column and would then replace this
character with the character in the second column of the same entry. For example, a would be replaced
with z. Decryption would do the opposite: it would go over each one of the characters of the
encrypted data, search for an entry in the table that contained this character in the second column and
would then replace this character with the character in the first column of the same entry. So, z would
be replaced with a. Someone that didn't know the mappings in this table would not know how to
perform this replacement to decrypt the encrypted data. This technique is known as a substitution
cipher. As you might have imagined, this technique is not completely secure as the encrypted data are
not really random. For example, some characters are more common than others and people can take
advantage of this to try and guess what the table looks like. Of course, there are other techniques that
are more secure, but I would have to explain many more things in order to be able to explain properly
how these work.

I think that's enough theory for now. I hope you are ready for some hacking!

You will now learn how you can find what computers a message will travel through when you send a
request to another computer on the Internet. To see the computers a request to Google will go through,
you can type in your terminal:

traceroute -I google.com

Note: Appendix A contains instructions on how to do this on Windows.

As you can see, we provide two kinds of inputs to the traceroute command:

-I indicates the protocol that will be used for the messages sent in the network. In this case, this is a
protocol called IGMP.
google.com indicates the final destination of the data.

When I run this command in my computer, I get the following result (you should get something
similar):

traceroute to google.com (216.58.213.14), 64 hops max, 72 byte packets


1 skyrouter (192.168.0.1) 4.990 ms 2.146 ms 1.744 ms
2 97e7fe34.skybroadband.com (151.231.254.52) 9.608 ms 6.854 ms 6.745 ms
3 be405.pr2.thlon.isp.sky.com (2.120.9.86) 8.927 ms 8.504 ms 16.749 ms
4 72.14.203.232 (72.14.203.232) 11.002 ms 10.200 ms 8.007 ms
5 74.125.253.31 (74.125.253.31) 7.457 ms 10.016 ms 6.682 ms
6 172.253.65.209 (172.253.65.209) 8.123 ms 57.298 ms 43.940 ms
7 * lhr25s25-in-f14.1e100.net (216.58.213.14) 7.388 ms 7.997 ms

This means in order for a message to travel from my computer to Google's server, it needs to go
through 6 intermediate routers. You can see the IP addresses for each one of them in parentheses and
the time it took for the message to travel to every one of those computers. So, if we were to draw that
in a diagram, it would look like the following:

We also said that in order for these computers to be able to route each message to its final destination,
they maintain these routing tables that paint the picture of the overall network. You can also see what
this routing table contains in your computer if you type the following command on a terminal:

netstat -rn

This will show you something like the following:


Destination Gateway Flags Netif Expire
default 192.168.0.1 UGSc en0
127 127.0.0.1 UCS lo0
127.0.0.1 127.0.0.1 UH lo0
...

As we learned before, this shows to which computer (Gateway) a message needs to be delivered in
order to arrive to the final computer (Destination).

We also saw previously that each domain name corresponds to an IP address (or more in some cases)
and there are some computers (called domain name servers) that we can ask to find out. You can also
do that in your terminal if you type the following command:

nslookup google.com

This requests the IP address for the domain name google.com and will return something like the
following:

Server: 192.168.0.1
Address: 192.168.0.1#53

Non-authoritative answer:
Name: google.com
Address: 216.58.210.238

The first section shows which domain name server was asked (192.168.0.1) and the second section
shows the IP address for the provided domain name (216.58.210.238).

Last, we mentioned that data are encrypted before sent into the network in many cases. You can
confirm that by using Wireshark to see the data that go into the network. If you visit a website that
supports HTTPS (most websites do nowadays), Wireshark will not show anything when you filter
using the label "http" as in the previous chapters. This is because data are not sent via http. However, if
you type "tls", you will see packets similar to the picture below. If you select one of those that have the
label "Application Data" in the Info column, you will see that the data are encrypted now and you
can't make any sense of it. By the way, TLS is the protocol that runs on top of HTTP and is responsible
for encrypting data before sending them in the network and decrypting them after receiving them
from the network.
That's just the beginning

So far, you have learned the following things:

how to program a computer.


what a computer is made of and how it works.
how computers communicate with each other.
what is the Internet and how it works.
how to build a web application that other people can use over the Internet.

Computer Science can be used to achieve much more than that. Computer Science contains so many
different fields and people spend years and years only to get a good understanding of one of them. So,
as you understand it would be impossible for me to cover everything in this book. However, my goal
is to make you understand all the cool things a Computer Scientist can do. You will then be able to go
and study more about the things that got you excited. In this last chapter, I will briefly show you some
of those things.

Most of the problems we solved in this book were relatively simple. In practice, you will be called to
solve problems that are harder to solve and some solutions might not be practical. As an example,
imagine if we wanted to keep all the students of a class in alphabetical order. This means everytime a
new student was enrolled in the class, we would need to add it in the right position, so that the
alphabetical order is maintained. Of course, the simplest solution would be to look at the existing list
of students one by one starting from the first student until we find the first student whose name is
alphabetically after the new student. Once we find that position, we can insert the new student there.
This works for small classes. But, imagine if this list was something else, say all the residents of
London. There are currently millions of people living in London, so as you can imagine this list would
be very large. As a result, adding a new resident in the list could be very slow, because the computer
would have to check every single resident from the beginning of the list and the new resident might
need to be placed near the end of the list. So, this solution would not be acceptable in this case, but we
could think of something smarter. For example, we could start searching in the middle of the list. If the
new resident was alphabetically before the person in the middle of the list, we would check the left
half of the list. Otherwise, we would check the right half of the list. We would keep doing that until we
found the right position in the list.

This would be much, much faster. To give you an idea, for a list containing 10 million residents,
finding the position for a new resident, we would need to check no more than 30 positions using this
technique. However, if we used the initial technique, the number of positions we would need to check
would depend on the final position of the new resident. In the worst case where the new resident
needs to be placed in the end of the list, we would need to check 10 million positions! I hope you
understand how much faster the second technique can be now. In order to explain, where the number
30 came from, I would need to spend some more time and I would also have to explain other concepts
from mathematics, but for now you can just trust me. The second technique is known as binary
search. Of course, there can be many different techniques to solve a problem like this. In Computer
Science, these are called algorithms.
Algorithms are typically used to perform calculations on some data. There are many different ways to
store that data and each one of those ways has pros and cons. For example, we can store a list of items
in two different ways. One way is to store the items of the list in a sequence and give each item in the
list a number. This is what you have seen so far in most cases. This is known as an array list. Another
way to store the items of a list is to have each item contain a link to the next item in the list. This is
known as a linked list.

As I mentioned before, each one of those ways has advantages and disadvantages. For example, when
using an array list we can find the item at a specific position in the list very quickly. This is because the
items are all stored in a sequence and we know the size of each item, so we can skip to the right place
in memory to find an item in a specific position. However, when we need to add a new item in the list,
that can be slow, because we have to move all the items one position to the right before adding the
new item in the list.
On the other hand, when using a linked list, we can add an item at a specific position in the list more
quickly as we don't have to move any items after it. All we have to do is to change the link of the
previous item to point to this new item and add a link from this item to the next item in the list.
However, items are not stored in sequence, so in order to find an item at a specific position in the list,
we need to go through the list item by item so this can be slow when the list is large.

These different ways to store data and access them are called data structures. Depending on the
problem we need to solve, we have to choose the right data structure that allows us to solve the
problem in the most efficient way. The process of studying problems, finding algorithms and data
structures to solve those problems, and comparing the algorithms to understand how fast they are is a
field of Computer Science on its own.
In the previous examples, we saw very simple scenarios where a single item was added on the list at a
time. However, in real life things can get much more complicated. There might be many different users
using our application and trying to add items in the list. If we allow everyone to access the list freely at
any point in time, we can have a lot of issues. For example, two users might end up adding a new
student at the same time, which can lead to the students being added in the wrong order. As a result,
the program needs to have some protections to ensure multiple users can try to add new students in
the class without creating problems. This phenomenon of multiple events happening at the same time
is called concurrency and concurrent programming is another field of Computer Science.

In the previous chapters, we explained what happens when we make a request to Google. We saw
how our message travels through the Internet to reach Google's server and what kind of programs this
server can run to handle our requests and give us a response back. However, this was an
oversimplified example. In practice, Google (and many other companies) have to run more than one
server. There are many different reasons for this, but here are two examples:

If there is a single server and this breaks down, then users would be unable to use Google. Google
can run multiple servers, so that if one of them crashes the other servers can keep handling
requests.
Google needs to store data about all the websites available on the Internet, so that it can give us the
relevant websites everytime we perform a Google search. However, this is a huge amount of data.
To give you a number, nowadays there are hundreds of billions of web pages on the Internet and
the data that Google needs to store is over 800,000,000,000,000 bits! As you can imagine, this is
much more than a single server can store. As a result, Google has to split all this data and store
different pieces on different servers.
Of course, doing the above can be very challenging. For instance, when you store the same data in
many different servers (as in the first example), you need to make sure new data are also added to all
the servers. Otherwise, people might see the wrong data by talking with a server that hasn't been
updated. When data is split and stored in many servers, you need to find an efficient algorithm that
decides how data are split and directs user requests to the servers that have the relevant data. A
system that contains many servers is called a distributed system and the process of building these
systems in the right ways is yet another field of Computer Science, known as distributed computing.

In most of our examples so far, we have seen how we can program computers to perform a task for us.
In many cases, the programs were very specific telling the computer exactly what to do. However, we
can also help computers learn how to solve a problem without explicitly telling them what to do. This
field of Computer Science is called machine learning, because we essentially tell the computer what is
the task we want to perform without giving them detailed instructions. Then, we give a lot of
examples of the task and the solution to the computer, so that it can gradually learn how to perform
this task. Let me try and explain through an example. Imagine we want to use a computer to
automatically check if there is a specific object in a photo, say a human face. There are machine
learning algorithms that allow us to do this by giving the computer a lot of photos and also telling the
computer if these photos contain this object or not. As the algorithm sees more and more photos, it
gradually understands better what a human face looks like in a photo. So, we can then provide a new
photo to the computer and it can tell us whether it thinks it contains a human face or not. The details
of how the computer can learn to perform this task are interesting, but quite complicated to explain
here. I will just say there is a lot of mathematics and statistics into it. So, if you think mathematics are
boring at school, wait until you see all the cool stuff you can do with it!

In this book, we only saw a couple of problems that can be solved by computers, such as maintaining
the students enrolled in a class. However, computers can be used to solve a lot of other interesting
problems in real life. Below are some examples:
We can use computers to process & analyse large amounts of biological data, which can be used for
many purposes such as finding new medicines. This field is known as bioinformatics and an
example project is the Human Genome Project, which attempted to sequence and map all of the
genes of the human species.
We can use computers to assist us with manual tasks, such as warehouse operations robots or
personal assistants. This field is known as robotics.
We can use computers to generate images or animations, such as the ones used in movies. This field
is known as computer-generated imagery (CGI).
As we have already seen, computers can be used to transfer information between two points. This
field is known as telecommunications. However, this can be much more than just transferring
information between two computers on Earth. For example, information could be transferred from
a satellite to a ground station.

Of course, these are just a few examples. If you are interested, you can find online many other practical
applications of Computer Science.
Appendix A - Getting setup
The steps below explain how you do some tasks you will need in this book, such as creating folders
and using a terminal, in different operating systems, such as MacOS and Windows.

Getting setup & performing other tasks in Windows


How to open a terminal window
If you want to open a terminal window, type "terminal" in the search bar at the bottom and select the
Windows Terminal application.

How to create a folder & open a terminal in a folder


If you want to create a new folder in your Desktop, you can right-click on your Desktop and select
New > Folder.
If you want to open a terminal inside a folder, you can right-click on a folder and select "Open in
Windows Terminal".
Printing the contents of a file in binary format
If you want to print the contents of a file (say students.txt) in binary format, you can type the
following in the terminal and press Enter:

$String = (Get-Content -Path "students.txt");


[system.Text.Encoding]::Default.GetBytes($String) | %
{[System.Convert]::ToString($_,2).PadLeft(8,'0') }

Feel free to just use the command as-is without trying to understand what it does, as that would
probably require another book!

Find the path data follow on the Internet


If you want to find the computers a request to Google will go through, you can do so by typing the
following in a terminal:

tracert google.com

Getting setup in MacOS


How to open a terminal window
If you want to open a terminal window, press Command+Space, type "terminal" in the search bar at
the bottom and select the Terminal application.
How to create a folder & open a terminal in a folder
If you want to create a new folder in your Desktop, you can right-click on your Desktop and select
"New Folder".

If you want to open a terminal inside a folder, you can right-click on a folder and select Services >
New Terminal at Folder.
If you don't see this option, you might need to enable it from your preferences. To do this, open System
Preferences, select Keyboard > Shortcuts > Services and tick the option "New Terminal at Folder"
under the section "Files and Folders".
Appendix B - Solutions to extra
challenges

Chapter 1
This is the solution to the challenge on chapter 1.

class Student:
def __init__(self, student_name, english_grade, geography_grade,
maths_grade):
self.name = student_name
self.sum_grade = english_grade + geography_grade + maths_grade

number_of_students_as_string = input("Give me the number of students in the


class: ")
number_of_students = int(number_of_students_as_string)

all_students = []

if number_of_students > 0:
for student_number in range(number_of_students):
student_name = input("Give me the name of the next student: ")
english_grade_as_string = input("Give me the grade this student got in
English: ")
english_grade = int(english_grade_as_string)
geography_grade_as_string = input("Give me the grade this student got in
Geography: ")
geography_grade = int(geography_grade_as_string)
maths_grade_as_string = input("Give me the grade this student got in
Mathematics: ")
maths_grade = int(maths_grade_as_string)
student = Student(student_name, english_grade, geography_grade,
maths_grade)
all_students.append(student)

best_student = all_students[0]
worst_student = all_students[0]

for student in all_students:


if student.sum_grade > best_student.sum_grade:
best_student = all_students[student_number]
if student.sum_grade < worst_student.sum_grade:
worst_student = all_students[student_number]
print("The student with the highest grade is: " + best_student.name)
print("The student with the lowest grade is: " + worst_student.name)

Chapter 3
This is the solution to the challenge on chapter 3.

from http.server import BaseHTTPRequestHandler,HTTPServer

class MyHandler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == "/students":
self.send_response(200)
self.send_header('Content-type','text/plain')
self.end_headers()
filename = "students.txt"
file = open(filename, "r")
file_contents = file.read()
student_names = file_contents.splitlines()
response = "The students in the class are: "
for name in student_names:
response = response + name + " "
self.wfile.write(response.encode())
elif self.path.startswith("/add_student/"):
student_name = self.path.replace("/add_student/", "")
filename = "students.txt"
file = open(filename, "a")
file.write(student_name)
file.write("\n")
self.send_response(200)
self.send_header('Content-type','text/plain')
self.end_headers()
response = "New student added successfully: " + student_name
self.wfile.write(response.encode())
else:
self.send_response(404)
self.end_headers()

server = HTTPServer(("", 8000), MyHandler)

print("Server has started!")


server.serve_forever()
Chapter 4
This is one solution to the challenge on chapter 4.

The server program (server.py) contains the following code:

from http.server import BaseHTTPRequestHandler,HTTPServer


from urllib.parse import parse_qs

page_file = open("class_page.html", "r")


class_page = page_file.read()

class MyHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-type','text/html')
self.end_headers()
filename = "students.txt"
file = open(filename, "r")
file_contents = file.read()
student_names = file_contents.splitlines()
students_list = "<ul>"
for name in student_names:
student_item = "<li>" + name + "</li>"
students_list = students_list + student_item
students_list = students_list + "</ul>"
class_page_final = class_page.replace("<students_list_placeholder>",
students_list)
self.wfile.write(class_page_final.encode())

def do_POST(self):
content_length_as_string = self.headers['Content-length']
content_length = int(content_length_as_string)
data = self.rfile.read(content_length).decode()
form_data = parse_qs(data)
student_name = form_data["name"][0]
filename = "students.txt"
file = open(filename, "a")
file.write(student_name)
file.write("\n")
self.send_response(302)
self.send_header('Location','/')
self.end_headers()

server = HTTPServer(("", 8000), MyHandler)

print("Server has started!")


server.serve_forever()

The file containing the HTML content of the page (class_page.html) contains the following content:
<html>
<head>
<title>Computer Science for kids - page</title>
<style>
body {
background-color: grey;
}
#welcome_box {
background-color: palegoldenrod;
font-family: fantasy;
padding: 10px;
margin: 20px;
border-style: outset;
}
#board {
background-color: darkgreen;
color: white;
font-family: cursive;
padding: 15px;
border-color: saddlebrown;
border-style: ridge;
border-width: medium;
box-shadow: 16px 6px 8px 4px black;
}
#form {
margin: 25px;
padding: 10px;
}
#button {
padding: 10px;
font-size: 15px;
cursor: pointer;
border-style: groove;
margin: 5px;
}
</style>
</head>

<body>
<div id="welcome_box">
<p>Welcome to the class of "Computer Science for kids"</p>
</div>
<div id="board">
<p>The students enrolled in this class are:</p>
<students_list_placeholder>
</div>
<div id="form">
<form method="post">
<div>
<label for="name">Provide the name of the new student:</label>
<input id="name" name="name" type="text">
<input id="button" type="submit" value="Add new student">
</div>
</form>
</div>
</body>
</html>

All of this works in the following way:

The HTML page contains a form in the end, where the user can write the name of a new student to
be added.
Once the user clicks the button "Enroll a new student", the browser performs an HTTP "POST"
request to the server, which contains the name of the student as a variable. If you want, you can use
Wireshark to see how that looks like.
When the server receives a "POST" request, it reads the data of the request from the rfile variable.
In order to do this, it first reads the size of the body, which is included in the header Content-
Length, and reads this exact number of characters from the body. This is then parsed into a map
using the function parse_qs. This map has an entry for the key name, which contains a list that has
a single item, the student name. So, form_data["name"][0] calculates the student name.
The name of the new student is written into the file that contains all the student names. Afterwards,
an HTTP response is sent back to the browser that has a status code of 302 and a Location header
with value /. This means the browser will redirect to the homepage, essentially refreshing the same
page and seeing the student that was just added to the class.

You might also like