We've so far been practicing the fundamentals of the language, such as variables, conditionals, loops, and methods. Let's now move on to look at some of the factors affecting the understandability of programs, and how errors are found.
A programmer often becomes blind to their code. Let's familiarize ourselves with this effect with the aid of the short video below. Count how many times the white-shirted players pass the ball between each other.
There's something else that also happens in the video that may go unnoticed at first. This effect is known as perceptual blindness, and is explained by the fact that as we focus on a specific task, our brains tend to filter out information that is irrelevant to that task. However, we don't always know what information is, in fact, essential and what is not - an example of this being when we study. Concentrating on a specific part of a study exercise can lead to relevant information being filtered out.
Fortunately, applying oneself to a given task lessens the occurrence of perceptual blindness. In other words, practice develops one's ability to distinguish between relevant and irrelevant information.
One way in which perceptual blindness manifests itself in programming practice is when concentrating on a specific part of a program draws attention away from seemingly correct, yet erroneous parts. For instance, while inspecting the correctness of a program's output, a programmer may fixate on the print statements, and mistakenly neglect some aspects of the logic.
Likewise, a programmer may focus on the most complicated aspect of a program featuring a loop, when in fact the error lies somewhere else completely. An example of this is the program below, which is used to calculate the average of user-inputted values. It contains an error, and when searching for it, the loop is typically the first target of focus.
values = 0
sum = 0
while True:
value = int(input("Provide a value, a negative value ends the program"))
if (value < 0):
break
values = values + 1
sum = sum + value
if (sum == 0):
print("The average of the values could not be calculated.")
else:
print("Average of values: " + str(sum / values))
Perceptual blindness is something that one cannot be eliminated completely. However, there are ways by which a programmer can lessen its effect - the first one being taking breaks, which requires that work is begun early. Code comments, proper naming of things, and "debugging" prints are additional examples of things that are also helpful.
Comments have many purposes, and one of them is explaining how the code works to oneself when searching for bugs. The execution of a relatively simple program is described below through the use of comments.
"""
Prints the numbers from ten to one.
Each number is printed on its own line.
"""
# We create an integer variable named value,
# assigning the value 10 to it.
value = 10
# The loop execution continues until
# the value of the variable named value is less than or equal to
# zero. The excution doesn't stop _immediately_ when the value zero
# is assigned to the variable, but only when the condition of the
# loop is evaluated the following time.
# This always happens after the loop has executed
while (value > 0):
# we print out the value in the variable and a new line
print(value)
# we decrement the value by one
value = value - 1
Comments have no impact on the execution of the program, i.e., the program works in the same way with the comments as it does without them.
The comment style displayed above that is intended for learning purposes is, however, too elaborate for real development, where the goal is for the source code to be self documenting. This means that the functionality of the program should be evident from the way classes, methods, and variables are named.
The example can be "commented out" by encapsulating the code into an appropriately named method. Below are two examples of methods that do this - one of the methods is more general in its purpose compared to the other. The more general method assumes, however, that the user knows which of the two parameters is assigned the higher value and which the lower.
def print_values_from_ten_to_one():
value = 10
while (value > 0):
print(value)
value = value - 1
def print_values_from_largest_to_smallest(start, end):
while (start >= end):
print(start)
start = start - 1
One required skill in programming is the ability to test and debug when searching for errors. The simplest way to search for errors is to use so-called print debugging, which in practice involves adding messages to certain lines of code. These messages are used to follow the flow of the program's execution, and can also contain values of variables that live in the program.
Let's inspect the program already familiar to us from the previous question that was used to calculate the average of non-negative values.
values = 0
sum = 0
while True:
value = int(input("Provide a value, a negative value ends the program"))
if (value < 0):
break
values = values + 1
sum = sum + value
if (sum == 0):
print("The average of the values could not be calculated.")
else:
print("Average of values: " + str(sum / values))
Had the program contained an error, print debugging could have been used to unravel its functionality by adding print statements in the appropriate places. The example below contains one possible example of a program containing print-debug statements.
values = 0
sum = 0
while True:
print("-- values: " + str(values) + ", sum: " + str(sum))
value = int(input("Provide a value, a negative value ends the program"))
if (value < 0):
print("-- value is negative, exiting loop")
break
values = values + 1
sum = sum + value
print("-- loop exited")
print("-- values: " + str(values) + ", sum: " + str(sum))
if (sum == 0):
print("The average of the values could not be calculated.")
else:
print("Average of values: " + str(sum / values))
When a program is executed multiple times with appropriate inputs the hidden error is often found. Coming up with relevant inputs is a skill in its own right. It's essential to test the so-called corner cases, i.e., circumstances where the program execution could be exceptional. An example scenario would be one where the user does not enter a single acceptable value or enters zeros or very large values.
In programming, we often encounter situations where we want to handle many values. The only method we've used so far has been to define a separate variable for storing each value. This is impractical.
word1 = "Hello"
word2 = "world."
word3 = "How"
word4 = "are"
word5 = "you"
word6 = "today?"
The solution presented above is useless in effect – consider a situation in which there are thousands of words to store.
Programming languages offer tools to assist in storing a large quantity of values. We will next take a peek at perhaps the single most used tool in Python, the list, which is used for storing many values.
The list tool offers various methods, including ones for adding values to the list, removing values from it, and also for the retrieval of a value from a specific place in the list. The concrete implementations – i.e., how the list is actually programmed – has been abstracted behind the methods, so that a programmer making use of a list doesn't need to concern themselves with its inner workings.
Creating a new list is done with the command list_name = []
, where the values to be stored in the list are included in the square brackets. We create a list for storing strings in the example below.
def main():
# create a list
my_list = []
# the list isn't used yet
The type of the my_list variable is list
. The list can hold multiple variable types at each of its indices, in contrast to other programming languages such as Java where all the variables stored in a given list are of the same type.
The next example demonstrates the addition of a few strings into a list containing strings. Addition is done with the list method append
, which takes the value to be added as a parameter. We then print the value at position zero. To retrieve a value from a certain position, you using my_list[2]
syntax, where the square brackets are given the place of retrieval as a parameter.
To call a list method you first write the name of the variable describing the list, followed by a dot and the name of the method.
def word_list_example():
# create the word list
word_list = []
# add two values to the word list
word_list.append("First")
word_list.append("Second")
# retrieve the value from position 0 of the word list, and print it
print(word_list[0])
if __name__ == '__main__':
word_list_example()
Negative : First
As can be seen, this method retrieves the first value from the list when it is given the parameter 0
. This is because list positions are counted starting from zero. The first value is found by word_list[0]
, the second by word_list[0]
, and so on.
def word_list_example():
# create the word list
word_list = []
# add two values to the word list
word_list.append("First")
word_list.append("Second")
# retrieve the value from position 1 of the word list, and print it
print(word_list[1])
if __name__ == '__main__':
word_list_example()
Negative : Second
You can also use negative indexing, which means beginning from the end. For example, -1 refers to the last item, -2 refers to the second last item and so on, as seen below:
def word_list_example():
# create the word list
word_list = []
# add two values to the word list
word_list.append("First")
word_list.append("Second")
word_list.append("Third")
# retrieve the value from the last position of the word list, and print it
print(word_list[-1])
if __name__ == '__main__':
word_list_example()
Negative : Third
Positive : Exercise - Third Element
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom
Positive : Exercise - Second Plus Third
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom
If you try to retrieve information from a place that does not exist on the list, the program will print an IndexError
. In the example below, two values are added to a list, after which there is an attempt to print the value at place two on the list.
def word_list_example():
# create the word list
word_list = []
# add two values to the word list
word_list.append("First")
word_list.append("Second")
# retrieve the value from position 1 of the word list, and print it
print(word_list[2])
if __name__ == '__main__':
word_list_example()
Since the numbering (i.e., indexing) of the list elements starts with zero, the program isn't able to find anything at place two and its execution ends with an error. Below is a description of the error message caused by the program.
Example output:
Traceback (most recent call last):
File "/path/to/example.py", line 13, in <module>
word_list_example()
File "/path/to/example.py", line 10, in word_list_example
print(word_list[2])
IndexError: list index out of range
Positive : A Place in a List Is Called an Index
Numbering places, i.e., indexing, always begins with zero. The list's first value is located at index 0, the second value at index 1, the third value at index 2, and so on. In programs, an index is often denoted with a variable called i
.
We'll next be examining methods that can be used to go through the values on a list. Let's start with a simple example where we print a list containing four values.
teachers = []
teachers.append("Simon")
teachers.append("Samuel")
teachers.append("Ann")
teachers.append("Anna")
print(teachers[0])
print(teachers[1])
print(teachers[2])
print(teachers[3])
Negative : Simon
Samuel
Ann
Anna
The example is obviously clumsy. What if there were more values on the list? Or fewer? What if we didn't know the number of values on the list?
The number of values on a list is provided by the list's len method which returns the number of elements the list contains. The number is an integer, and it can be used as a part of an expression or stored in a variable for later use.
list = []
print("Number of values on the list: " + str(len(list)))
list.append("First")
print("Number of values on the list: " +str(len(list)))
values = len(list)
list.append("Second")
print("Number of values on the list: " + str(values))
Negative : Number of values on the list: 0
Number of values on the list: 1
Number of values on the list: 1
Positive : Exercise - List size
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom
Let's make a new version of the program that prints each index manually. In this intermediate version we use the index
variable to keep track of the place that is to be outputted.
teachers = []
teachers.append("Simon")
teachers.append("Samuel")
teachers.append("Ann")
teachers.append("Anna")
index = 0
if index < len(teachers):
print(teachers[index]) # index = 0
index = index + 1 # index = 1
if index < len(teachers):
print(teachers[index]) # index = 1
index = index + 1 # index = 2
if index < len(teachers):
print(teachers[index]) # index = 2
index = index + 1 # index = 3
if index < len(teachers):
print(teachers[index]) # index = 3
index = index + 1 # index = 4
if index < len(teachers):
# this will not be executed since index = 4 and len(teachers) = 4
print(teachers[index])
index = index + 1
We can see that there's repetition in the program above.
We can convert the if
statements into a while
loop that is repeated until the condition index < len(teachers)
no longer holds (i.e., the value of the variable index
grows too great).
teachers = []
teachers.append("Simon")
teachers.append("Samuel")
teachers.append("Ann")
teachers.append("Anna")
index = 0
# Repeat for as long as the value of the variable `index`
# is smaller than the size of the teachers list
while index < len(teachers):
print(teachers[index])
index = index + 1
Now the printing works regardless of the number of elements.
The for-loop we inspected earlier used to iterate over a known number of elements is extremely handy here. We can convert the loop above to a for
-loop, after which the program looks like this.
teachers = []
teachers.append("Simon")
teachers.append("Samuel")
teachers.append("Ann")
teachers.append("Anna")
for index in range(len(teachers)):
print(teachers[index])
Negative : Simon
Samuel
Ann
Anna
Python actually has a "better" way of accessing list items in a for loop which is different to certain other languages. You can use the syntax for item in list
to access each item like so:
teachers = ["Simon","Samuel","Ann","Anna"] # an alternative way of creating a list
for teacher in teachers:
print(teacher)
There are certain reasons why you'd use range(len(list))
but most of the time we will use the latter of these two options.
Let's consider using a list to store integers. The functionality is largely the same as in the previous example.
numbers = [1,2,3,4] # an alternative way of creating a list
for number in numbers:
print(number)
Negative : 1
2
3
4
Printing the numbers in the list in reverse order would also be straightforward.
numbers = [1,2,3,4]
index = len(numbers) - 1
while index >= 0:
number = numbers[index]
print(number)
index = index - 1
Negative : 4
3
2
1
Try and recreate the previous example with the for loop!
Positive : Notice about the following exercises
The next exercises are meant for learning to use lists and indices. Even if you could complete the exercises without a list, concentrate on training to use it. The functionality in the exercises is to be implemented after reading the input numbers.
Positive : Exercise - Last in list
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom
Positive : Exercise - First and last
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom
Positive : Exercise - Remember these numbers
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom
Positive : Exercise - Only these numbers
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom
Positive : Exercise - Greatest in list
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom
Positive : Exercise - Index of
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom
Positive : Exercise - Index of smallest
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom
Positive : Exercise - Sum of a list
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom
Positive : Exercise - Average of a list
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom
In Python, list's methods clear()
, pop()
, and remove()
are used to remove items (elements) from a list.
my_list = list(range(10))
print(my_list)
my_list.pop(0)
print(my_list)
my_list.pop(3)
print(my_list)
Negative : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 5, 6, 7, 8, 9]
Note that the list is edited in place, meaning that the value of my_list
is overwritten by pop()
.
You can remove the first item from the list where its value is equal to the specified value with remove()
.
my_list = ['Alice', 'Bob', 'Charlie', 'Bob', 'Dave']
print(my_list)
my_list.remove('Alice')
print(my_list)
my_list.remove('Bob')
print(my_list)
Negative : [‘Alice', ‘Bob', ‘Charlie', ‘Bob', ‘Dave']
[‘Bob', ‘Charlie', ‘Bob', ‘Dave']
[‘Charlie', ‘Bob', ‘Dave']
Note that if the list contains more than one matching the specified value, only the first one is deleted.
The list method in can be used to check the existence of a value in the list. The method receives the value to be searched as its parameter, and it returns a boolean type value (True
or False
) that indicates whether or not that value is stored in the list.
list = ["First","Second","Third"]
print("Is the first found? " + str("First" in list))
found = "Second" in list
if found:
print("Second was found")
# or more simply
if "Second" in list:
print("Second can still be found")
Negative : Is the first found? true
Second was found
Second can still be found
Positive : Exercise - On the list
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom
Like other variables, a list can be used as a parameter to a method too. When the method is defined to take a list as a parameter, the type of the parameter is defined as the type of the list and the type of the values contained in that list. Below, the method print
prints the values in the list one by one.
def printout(list):
for value in list:
print(value)
my_list = ["One","Two"]
printout(my_list)
Negative : One
Two
The chosen parameter in the method definition is not dependent on the list that is passed as parameter in the method call. In the program that calls printout
, the name of the list variable is list
, but inside the method printout
the variable is called my_list
– the name of the variable that stores the list could also be printables
, for instance.
It's also possible to define multiple variables for a method. In the example the method receives two parameters: a list of numbers and a threshold value. It then prints all the numbers in the list that are smaller than the second parameter.
def print_smaller_than(numbers, threshold):
for number in numbers:
if (number < threshold):
print(number)
list = [1,2,3,2,1]
print_smaller_than(list, 3)
Negative : 1
2
2
1
Positive : Exercise - Print in range
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom
As before, a method can also return a value. The methods that return values have the return
command. The method below returns the length of the list, although is a completely redundant method.
def length(list):
return len(list)
You can also define own variables for methods. The method below calculates the average of the numbers in the list. If the list is empty, it returns the number -1.
def average(numbers):
if (len(numbers) == 0):
return -1
sum = 0
for number in numbers:
sum = sum + number
return sum / len(numbers)
Positive : Exercise - Sum
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom
Earlier we have used integers, floating point numbers, etc. as method parameters. When variables such as int
are used as method parameters, the value of the variable is copied for the method's use. The same occurs in the case that the parameter is a list.
Lists, among practically all the variables that can store large amounts of information, are reference-type variables. This means that the value of the variable is a reference that points to the location that contains the information.
When a list (or any reference-type variable) is copied for a method's use, the method receives the value of the list variable, i.e., a reference. In such a case the method receives a reference to the real value of a reference-type variable, and the method is able to modify the value of the original reference type variable, such as a list. In practice, the list that the method receives as a parameter is the same list that is used in the program that calls the method.
Let's look at this briefly with the following method.
def remove_first(numbers):
if len(numbers) == 0:
return
numbers.pop(0)
numbers = [3,2,6,-1]
print(numbers)
remove_first(numbers)
print(numbers)
remove_first(numbers)
remove_first(numbers)
remove_first(numbers)
print(numbers)
Negative : [3, 2, 6, -1]
[2, 6, -1]
[]
Positive : Exercise - Remove last
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom
Positive : NumPy Arrays
We've gotten familiar with the list, which has a lot of functionality to make the life of a programmer easier. Perhaps the most important is about adding elements. From the point of view of the programmer, the size of the list is unlimited. In reality there are no magic tricks in the list – they have been programmed like any other programs or tools offered by the programming language. When you create a list, a limited space is reserved in the memory of the computer. When the list runs out of space, a larger space is reserved and the data from the previous space is copied to the new one.
Even though the list is simple to use, sometimes we need an alternative way of storing data, the NumPy array. The main benefits of using NumPy arrays is, in general, smaller memory consumption and better runtime behaviour. We will not dive any deeper into NumPy arrays in this course, but you should be aware of their existence and, in particular their applicability to data science and scientific computing.
You can read more here.
Let's first revise what we already know about strings and see how to split them. Below we create a string variable magic_word
, that contains value "abracadabra"
.
magic_word = "abracadabra"
Passing a string as a parameter to a print command (or, for that matter, any method that takes a string parameter) happens in the familiar way:
magic_word = "abracadabra"
print(magic_word)
Negative : abracadabra
You can read a string using the input-method offered by Python. The program below reads the name of the user and prints it:
# reading a line from the user and assigning it to the name variable
name = input("What's your name?")
print(name)
Negative : What's your name?
User: <Vicky>
Vicky
Strings can also be concatenated. If you place a +
-operator between two strings, you get a new string that's a combination of those two strings. Be mindful of any white spaces in your variables!
greeting = "Hi "
name = "Lily"
goodbye = " and see you later!"
phrase = greeting + name + goodbye
print(phrase)
Negative : Hi Lily and see you later!
Positive : Exercise - Print thrice
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom
Positive : Exercise - Is it true?
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom
Positive : Exercise - Login
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom
You can split a string to multiple pieces with the split
-method of the string class. The method takes as a parameter a string denoting the place around which the string should be split. The split
method returns al ist of the resulting sub-parts. In the example below, the string has been split around a space.
text = "first second third fourth"
pieces = text.split(" ")
print(pieces[0])
print(pieces[1])
print(pieces[2])
print(pieces[3])
print()
for piece in pieces:
print(piece)
Negative : first
second
third
fourth
first
second
third
fourth
Positive : Exercise - Line by line
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom
Positive : Exercise - AV Club
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom
Splitting strings is used particularly when the data is of a fixed format. This refers to data that adheres to some predefined format. An example of this of this is the comma-separated values (csv
) format, where commas are used to separate values. Below you'll find an example of data in csv form containing names and ages. The first column contains names and the second one ages. The columns are separated by a comma.
Negative : sebastian,2
lucas,2
lily,1
Let's assume the user enters the data above row by row, ending with an empty line.
A program to print the names and ages looks like the following:
while True:
textinput = input()
if (textinput == ""):
break
pieces = textinput.split(",")
print("Name: " + pieces[0] + ", age: " + pieces[1])
Negative : User: <sebastian,2>
Name: sebastian, age: 2
User: <lucas,2>
Name: lucas, age: 2
User: <lily,1>
Name: lily, age: 1
Positive : Exercise - First Words
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom
In the exercises above, we actually implemented a very simple decrypting method for secret messages. One variant of these hidden messages consists of the first character of each line. Let's continue along the same theme! You can get a character at a specified index of a string very similarly to how you access the index of a list, with the []
notation.
text = "Hello world!"
character = text[0]
print(character)
Negative : H
We've printed strings in the examples above. Some of the data contained in a fixed-format string can be numerical. In the previous data we used that contained names and ages, the ages were integers.
Negative : sebastian,2
lucas,2
lily,1
Splitting a string always produces a list of strings. If the text is of fixed format, we can assume the data in a specific index to always be of the a specific type – e.g., in the example above, age at index 1 is an integer.
The program below computes the sum of ages in this fixed format data. In order to compute the sum, the age must first be converted to a number (the familiar command int()
.
sum = 0
while True:
textinput = input()
if (textinput == ""):
break
parts = textinput.split(",")
sum = sum + int(parts[1])
print("Sum of the ages is " + str(sum))
Negative : User: <sebastian,2>
User: <lucas,2>
User: <lily,2>
Sum of the ages is 5
We can write a program to compute the average of the ages in the same way:
sum = 0
count = 0
while True:
textinput = input()
if (textinput == ""):
break
parts = textinput.split(",")
sum = sum + int(parts[1])
count += 1
if (count > 0):
print("Age average: " + str(sum / count))
else:
print("No input.")
Negative : User: <sebastian,2>
User: <lucas,2>
User: <lily,1>
Age average: 1.66...
Positive : Exercise - Age of the Oldest
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom
Positive : Exercise - Name of the Oldest
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom
In the next exercise you'll be asked for the length of the names. You can find out the length of a string exactly as for a list, with the len()
-method:
word = "nonagenarian"
length = len(word)
print("The length of the word " + word + " is " + str(length))
Negative : The length of the word equisterian is 11
Positive : Exercise - Personal details
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom
Very often programs handle data. This data must be stored somewhere for the duration of program execution — creating a new variable for each piece of data would be very labour intensive and make the code hard to understand. In this part we have taken the first steps towards effective data management: by using lists and arrays we can store almost unlimited amounts of data for the duration of program execution. We have also practiced string handling and splitting strings into smaller substrings.