A list in Python is a heterogeneous container for multiple items.
A list is a similar data structure like an array in many other languages (like C++, Java or C#), but since Python does not support arrays, we have lists.
Let's define a list of neighbouring countries for Hungary. The initial items of a list are defined between brackets.
neighbours = ['Austria', 'Slovakia', 'Ukraine', 'Romania', 'Serbia', 'Croatia', 'Slovenia']
print(neighbours)
Let's check the type of the neighbours
variable.
print(type(neighbours))
The items of a list can be accessed by the numerical indexes. (The first item is indexed with zero.)
print(neighbours[0])
print(neighbours[1])
We can also access a range of elements:
print(neighbours[2:5])
The end index is exclusive, which means that only the countries with index 2-4 was included in the result above.
By leaving out the end index, the range will go on to the end of the list:
print(neighbours[2:])
By leaving out the start index, the range will start at the first item:
print(neighbours[:5])
By omitting both the start and the end index, the range will cover all elements:
print(neighbours[:])
Note: this is very similar like how we worked with strings, since strings can be treated like list of characters.
The number of items in a list (its length) can also be easily fetched:
print(len(neighbours))
Lists are mutable, meaning the items themselves and the number of items it contains can change dynamically after its initial definition. We can remove elements:
neighbours.remove('Slovakia')
print(neighbours)
Add new ones:
neighbours.append('Czechoslovakia')
print(neighbours)
The elements can also be removed from or inserted to a specific location:
neighbours.pop(3) # removes Serbia, as its index is 3
del neighbours[3] # removes Croatia, as its index is 3, after we removed Serbia
neighbours.insert(3, 'Yugoslavia') # adds Yugoslavia on the 3rd index
print(neighbours)
Copying a list can be a bit tricky, because assigning a list to a new variable does not make a copy of a list, instead the new variable will just be an alias to the original list.
To make a real copy of a list, we can either use the copy()
method of the list or use the range accessor to select and copy all elements to a new list.
alias_list = neighbours # this is just an alias
copied_list_1 = neighbours.copy() # this is a real copy
copied_list_2 = neighbours[0:len(neighbours)] # this is also real copy
copied_list_3 = neighbours[:] # this also copies all elements
# Clear all elements from the original list
neighbours.clear()
print(neighbours) # this shall be empty
print(alias_list) # this shall also be empty
print(copied_list_1) # this shall contain the elements
print(copied_list_2) # this too
print(copied_list_3) # this too
Lists have further useful methods, we can e.g. sort or reverse the elements of a list:
neighbours = ['Austria', 'Slovakia', 'Ukraine', 'Romania', 'Serbia', 'Croatia', 'Slovenia']
print('Original list: {0}'.format(neighbours))
neighbours.sort()
print('Sorted list: {0}'.format(neighbours))
neighbours.reverse()
print('Reversed list: {0}'.format(neighbours))
See the documentation for a complete overview.
The loop control flow is also called iteration or repetition statement and provides a way to execute the same code block (call the core of the iteration) until a condition is meet.
for
loop: Iterating through a sequence¶When using a for
loop we introduce a new variable which will iterate over all elements of a list (or later other data structures containing multiple items) and take the value of the next item in each iteration.
Let's iterate over the values of the europe
list with a variable country
:
europe = ['Albania', 'Andorra', 'Austria', 'Belgium', 'Bosnia and Herzegovina', 'Bulgaria', 'Czech Republic', 'Denmark', 'United Kingdom', 'Estonia', 'Belarus', 'Finland', 'France', 'Greece', 'Netherlands', 'Croatia', 'Ireland', 'Iceland', 'Kosovo', 'Poland', 'Latvia', 'Liechtenstein', 'Lithuania', 'Luxembourg', 'Macedonia', 'Hungary', 'Malta', 'Moldova', 'Monaco', 'Montenegro', 'Germany', 'Norway', 'Italy', 'Portugal', 'Romania', 'San Marino', 'Spain', 'Switzerland', 'Sweden', 'Serbia', 'Slovakia', 'Slovenia', 'Ukraine']
for country in europe:
print(country)
This new variable introduced for iterating over the elements can be named anything. (Applying the same rules of course for naming variables discussed in Chapter 1.)
for anything in europe:
print(anything)
Note the identation of the print
statement, which means that the print
statement is "inside" the for
loop and will be executed on each iteration.
for country in europe:
print(country)
print("This will be printed after each country")
print("This will be printed only once after all countries are printed")
The workflow diagram of the for loop:
range
function¶range()
is a function to create integer sequences, which can be converted to lists. We give the start and end value as arguments to the function. The end value is exclusive.
print(list(range(1, 10)))
We may only give one argument to the function, which will be the end value. The start value will be 0 in such a case.
print(list(range(8)))
As an optional, third argument, the step value can be passed, which defines the incrementation between the values of the resulted list.
print(list(range(1, 20, 2)))
We can use the range()
function to print both the index and the name of each country:
print("Number of European countries: {0}".format(len(europe)))
for index in range(len(europe)): # length of a sequence
print("The {0}th country in the list is: {1}".format(index, europe[index]))
Remark: ranges are so called "lazy" objects, because they do not generate every number they contain when we create them. Instead they only produce the contained numbers as we need them when looping over them; or when we convert them to lists.
Task: Print all the countries in the europe
list, which start with letter C
:
for country in europe:
if country[0] == 'C':
print(country)
Help: you have to place a conditional statement (if
) inside an iterative statement (for
) and combine them.
while
loop: Keep doing until condition no longer holds¶Use for
when you know the exact number of iterations; use while
when you do not (e.g., checking convergence).
x = 123.0
while x > 1:
print(x)
x = x / 2
#x /= 2
The workflow diagram of the while loop:
Task: write a program that requests some input from the user until the user types in: enough. Then the execution of the program shall stop.
user_input = input('Type in something: ')
while user_input != 'enough':
user_input = input('Type in something: ')
Task: write a program that requests number from the user until the user types in: enough. Then the program shall list all the previously inputted numbers in the same order. Invalid (not numeric) inputs shall be skipped, also displaying a warning message that an incorrect value was typed in. Sample input and output:
Next number: 10
Next number: 8
Next number: apple tree
It is not a number, skipped!
Next number: 42
Next number: -4
Next number: enough
Given numbers: [10, 8, 42, -4]
numbers = []
user_input = input('Next number: ')
while user_input != 'enough':
try:
num = int(user_input)
numbers.append(num)
except:
print('It is not a number, skipped!')
user_input = input('Next number: ')
print('Given numbers: {0}'.format(numbers))
Help: define an empty list before requesting user input iteratively. Add the user given numbers to the list (append
). Then in the end you only need to display the items of the list.
break
and continue
¶break
means get out of the loop immediately. Any code after the break
will NOT be executed.
Compare the following 2 solutions for listing all divisors versus only the first divisor of a number:
number = int(input("Input an integer number: "))
for i in range(2, number+1):
if number % i == 0:
print("{0} is a divisor of {1}".format(i, number))
else:
print("{0} is NOT a divisor of {1}".format(i, number))
number = int(input("Input an integer number: "))
for i in range(2, number+1):
if number % i == 0:
print("The first divisor of {0} is {1}".format(number, i))
break # NOTE the break statement here!
else:
print("{0} is NOT a divisor of {1}".format(i, number))
continue
means get to the next iteration of loop. It will stop the current iteration and continue to the next.
Compare the following 2 solutions for listing all divisors of a number:
number = int(input("Input an integer number: "))
for i in range(1, number+1):
if number % i == 0:
print("{0} is a divisor of {1}".format(i, number))
number = int(input("Input an integer number: "))
for i in range(1, number+1):
if number % i != 0:
continue # if i NOT a divisor of number, then we continue the iteration with the next number
print("{0} is a divisor of {1}".format(i, number))
NOTE: break
and continue
can also be used within while
loops.
Task: Test whether a number is a prime
Request an integer number from the user.
Decide whether the number is a prime number or not and display your answer.
import math
number = int(input("Number to check: "))
is_prime = True
if number < 2:
# Handle 0 and 1 as a special case
is_prime = False
else:
# Numbers >= 2 are tested whether they have any divisors
for i in range(2, int(math.sqrt(number) + 1)):
print("Testing divisor {0}".format(i))
if number % i == 0:
# If we found a divisor, we can stop checking, because the number is NOT a prime
is_prime = False
break
if is_prime:
print("{0} is a prime".format(number))
else:
print("{0} is NOT a prime".format(number))
Random numbers can be generated with the random
module.
Execute the code cell below multiple times to get different results:
import random
number = random.randint(1, 10)
print(number)
Generate 10 numbers between 1 and 100:
numbers = []
for i in range(10):
numbers.append(random.randint(1, 100))
print(numbers)
This is advanced level remark, which is interesting, but not mandatory on this introductory course.
There is no real randomicity in computer science (unless you build a device which measures e.g. cosmic radiation). These numbers generated are so called pseudo-random numbers. They are generated by a deterministic mathematical algorithm along a uniform distribution.
The following algorithm will always generate the same numbers regardless how many times you execute it, because we seed the algorithm a fix initial value before each random generation:
random.seed(42)
numbers = []
for i in range(10):
numbers.append(random.randint(1, 100))
print(numbers)
By default the algorithm is only seeded once, with a value related to the milliseconds passed since the start of the computer. Hence it will look like real "random" numbers.
Task: Dice roll
Write a loop which generates a dice roll (1-6) in every iteration. Run the loop 100 times and calculate the average value of the dice rolls! What is the difference against the expected value?
How does it change if you execute the dice roll 1000 times?
Hint: instead of adding the generated numbers together one-by-one, you may use the sum(my_list)
function to calculate the accumulated value of a list of numbers.
Built-in functions like sum
will be further discussed in Chapter 4.
import random
numbers = []
for i in range(0, 1000):
dice_roll = random.randint(1, 6)
numbers.append(dice_roll)
avg = sum(numbers) / len(numbers)
print('Average value: {0}'.format(avg))
print('Expected value: 3.5')