The for loop#
Learning Outcomes#
Iterate using a
forloop.Iterate through a list.
Understand
enumerate()andzip().Iterate using the
range()function.Use nested loops.
Understand
continueandbreak.
Prerequisites:#
Iterating with a list#
You will often find tasks that you need to do over and over again, such as the same calculation on multiple data points. To save writing out the same equation over and over again, you can instead use loops to do thousands of calculations incredibly quickly, making you a lab data processing wizard!
Just like with if statements, for loops require specific syntax.
for number in list_of_numbers:
do action
forindicates that we will begin a loop.numberis a new variable we are defining, describing each item within the list of numbers in turn. For each loop, it will automatically redefine to refer to the next item in the list.in list_of_numbersindicates that we want to repeat the action in the loop once per item in the listlist_of_numbers.:signals the beginning of the loop.The actions within the loop are indented with the
<tab>key. All lines of code indented just below theforline gets repeated within the loop. When the code is no longer indented, the loop is over. You can write code of any length within aforloop as long as you respect its indentation.
Here is an example:
molecules = ["H2", "H2O", "CH4"]
for mol in molecules:
print(mol)
# This is no longer indented, and represents the rest of the program
The cell above prints three strings despite having only used a single print() statement. Let’s look at this closer.
We begin by defining a list of strings:
molecules = ["H2", "H2O", "CH4"]
We start a loop with the line:
for mol in molecules:
forindicates that we will begin a loop.molis a new variable we are defining, describing each item within the list of molecules in turn. In the first loop it is a variable referring to"H2", in the second loop it refers to"H2O", and in the last loop it refers to"CH4".in moleculesindicates that we want to do repeat the operations in the loop once per molecule in the listmolecules.:signals the beginning of the loop.
We print the contents of the mol variable to the screen using:
print(mol)
While we are within this loop, the variable mol will adopt the values of each element of the list molecules in sequence, changing at every loop. This is why print(mol) is effectively becoming print("H2"), print("H2O"), and print("CH4") one after another.
When the code is no longer indented, the loop is over. You can write Python code of any length inside a for loop as long as you respect its indentation.
Iterating to perform many maths operations
for loops can iterate through lists containing both strings and numbers. By iterating through lists of floats or integers, you can perform maths on each item in turn. The new values can then be saved (by appending to a new list) for later.
# Create a list of pressures in mmHg
pressure_mmHg = [2945.01, 1671.43, 908.56, 625.3]
# Create an empty list which we will populate with pressures in bar
# This must be created outside the loop
pressure_bar = []
for value in pressure_mmHg:
# Calculate pressure in bar and check with a print() statement
new_pressure = value / 750.06
print(new_pressure)
# Add the calculated pressure in bar to the empty list
pressure_bar.append(new_pressure)
# We can now use the list of pressures in bar throughout the rest of the program
print(pressure_bar)
In this piece of code, we are converting pressure in mmHg to bar. Each value in the list pressure_mmHg is converted into bar, printed, and appended into a new list which contains all the associated values in bar. This list can then be used throughout the rest of the code.
The empty list pressure_bar is first defined outside of the loop. If it was defined inside the loop, it would be re-defined as empty on every loop, and by the end would only contain the final value. Try it yourself to see.
Iterating through a string
for loops can also iterate over characters in a string.
string = "hydrogen2oxygen"
for letter in string:
print(letter)
This has limited use for chemistry purposes, but it is good to be aware of.
Iterating using nested lists
You can also iterate over nested lists, keeping in mind that each nested list is the item being referred to when assigning your temporary variable. You can refer to items within the nested list using square brackets, or nested loops (more on that later). Try out this code:
bimolecular_atom_masses = [[1.008, 1.008], [12.011, 15.999], [15.999, 15.999]]
molecule_mass = []
for molecule in bimolecular_atom_masses:
total_mass = molecule[0] + molecule[1]
print(total_mass)
molecule_mass.append(total_mass)
print(molecule_mass)
In this program, each nested list in bimolecular_atom_masses is assigned the temporary variable name molecule. To refer to an item within this nested list, you must refer to it as molecule[0] or molecule[1], where the square bracketed number refers to the item’s position within the list. Remember that Python is zero-indexed, so the first item in a list is called ‘0’, the second item is called ‘1’ and so on.
Reminder: To refer straight to an item in a nested list, you can use double sets of square brackets. For example, to refer to the float 12.011 in the above list, you can call it with bimolecular_atom_masses[1][0]. This calls the item in position zero of the list in position 1. Remember that lists are zero-indexed, and so the first item in a list always has the index 0.
Combining loops and conditional statements
You can test a condition by using an if statement within your for loop. For example, to find outliers in your data.
This is an imaginary data set, with standard deviation and the mean already found. We want to identify any points that are more than 3 standard deviations above the mean.
data_points = [5.6, 11.4, 4.1, 3.4, 2.5, 37.2, 2.9, 12.2, 14.9, 9.6]
standard_deviation = 5.5
mean = 8.6
for point in data_points:
if point > mean + 3 * standard_deviation:
print(point, " is an outlier")
elif point < mean + 3 * standard_deviation:
print(point, "is an outlier")
The output is: 37.2 is an outlier
We could also use loops to calculate the mean and standard deviation of a dataset in the first place!
Exercise 1: Write a basic loop
Write a code that prints out each item in the following list, using a for loop. Then answer the following True/False questions.
gas_list = [‘Nitrogen’, ‘Oxygen’, ‘Neon’]
The loop will print the name of each gas on the same line.
The variable
gas_listdoes not change on each loop.The
forstatement does not need a colon.
Click to view answer
Here is an example of the code you should have written:
gas_list = ['Nitrogen', 'Oxygen', 'Neon']
for gas in gas_list:
print(gas)
False. Each gas name will be printed on a separate line.
True. The variable
gas_listis the same throughout the loop.False. The end of every
forstatement requires a colon, otherwise Python will throw an error.
Exercise 2: Understanding a Loop
In the lab you are making methyl benzoate using the scheme below, starting with 3 g of benzoic acid. Five scientists repeat the reaction, and get the following yields of product:
Product yields: 2.5 g, 2.7 g, 3.1 g, 1.6 g, 4 g.
Use your understanding of for loops to work out the percentage yields for each person. Use an if statement within the loop to test the following conditions:
Percentage yield is smaller than 50 % - print a statement that the yield is poor.
Percentage yield is between 50 % and 100 % - print that the yield is good.
Percentage yield is over 100 % - print a statement that the product is likely impure.
The theoretical yield is equal to the starting mass ÷ 122, then × 136.
Click to view answer
product_masses = [2.5,2.7,3.1,1.6,4] # grams
starting_mass = 3 # grams
theoretical_yield = (starting_mass/122)*136 # grams
# For each item in the list product_masses
for product_mass in product_masses:
# Calculate percentage yield and print to no decimal places
percent_yield = (product_mass / theoretical_yield) * 100 # %
print(f"Percent yield: {percent_yield:.0f}%")
# Use conditional statements to assign purity
if percent_yield < 50:
print("Poor yield")
elif percent_yield >= 50 and percent_yield < 100:
print("Good yield")
elif percent_yield >= 100:
print("Likely Impure")
Note the use of :.0f to give the percentage to no decimal places.
Exercise 3: Debugging a loop
You have a series of temperature measurements from your reaction in degrees C and you want to print all of them out in Kelvin. One of your colleagues writes some code but it doesn’t seem to be working. Can you identify the problem and fix the code?
# Your colleagues code # The code below is not working. Can you fix it? temperature_measurements = [27.3, 28.1, 26.9, 27.5, 28.0] for temperature_C in temperature_measurements: temperature_K = temperature_C + 273.15 print(f"Temperature: {temperature_K}°C")
Click to view answer
Remember that code you want to run every time the loop repeats need to be indented. In the example the print statement is not indented and so is not executed each time, but only once when the loop is finished. You can fix the code just by indenting the print statement with the tab key.
temperature_measurements = [27.3, 28.1, 26.9, 27.5, 28.0]
for temperature_C in temperature_measurements:
temperature_K = temperature_C + 273.15
print(f"Temperature: {temperature_K}°C")
Using enumerate()#
Each entry in a list has two things associated with them: the index of the entry and the value of the entry.
Python is zero-indexed, meaning that each value in a list carries an index from 0 upwards. For example, in the list ["H", "He", "Li", "Be"], “H” is at index position 0, “He”, is at index position 1, “Li” is at index position 2, and “Be” at index position 3. Sometimes it is useful to know the index for a particular list or item in a list. Python allows us to find out the index while we iterate through a list using the function enumerate()
The enumerate() function assigns each element in a list a number, from 0 upwards. This is the index, which can then be referred to to call items from that list. This is a good way of identifying where in a list a certain item is. The basic syntax of enumerate() is:
for i, j in enumerate(list):
iis a temporary variable referring to the index number that is being assigned (i for index).jis the item value within the list.listis the list we are assigning index values to.
Here is an example:
atom_names = ["H", "He", "Li", "Be"]
for index, atom in enumerate(atom_names):
print(atom, "is at position" , index, "in the list")
Resulting in the following output:
H is at position 0 in the list
He is at position 1 in the list
Li is at position 2 in the list
Be is at position 3 in the list
Note that H is at position 0, not 1. This goes back to Python’s 0-indexing, where ordered items are assigned values starting from 0, not 1.
Using enumerate to find a list index
You have a very long list of amino acids, and you want to find the position of the amino acid “Proline”.
natural_amino_acids = ["Alanine", "Arginine", "Asparagine", "Aspartic acid", "Cysteine", "Glutamic acid", "Glutamine", "Glycine", "Histidine", "Isoleucine", "Leucine", "Lysine", "Methionine", "Phenylalanine", "Proline", "Serine", "Threonine", "Tryptophan", "Tyrosine", "Valine"]
for index, amino_acid in enumerate(natural_amino_acids):
if amino_acid == "Proline":
print(f"Proline is at position {index} in the list.")
The code will output:
Proline is at position 14 in the list
If you had another list of corresponding masses, you could then identify which mass belonged to proline and set it as a useful variable. For example, from a the list of masses called amino_acid_masses you could retrieve and define the variable mass_proline = amino_acid_masses[14].
Using enumerate to iterate through two lists
You could use enumerate() to iterate through two or more lists at once.
atom_names = ["H", "He", "Li", "Be"]
atom_masses = [1.008, 4.003, 6.94, 9.012] # g mol-1
atom_neutrons = [0, 2, 4, 5]
for index, atom in enumerate(atom_names):
print(f"The mass of {atom} with {atom_neutrons[index]} neutrons is {atom_masses[index]}")
The result is:
The mass of H with 0 neutrons is 1.008
The mass of He with 2 neutrons is 4.003
The mass of Li with 4 neutrons is 6.94
The mass of Be with 5 neutrons is 9.012
In this case, all three lists are associated item-wise, meaning the element at index 0 in atom_names is related to the mass at index 0 of atom_masses and the number at index 0 in atom_neutrons. You only need to enumerate one of these lists (in this case, atom_names) to be able to reference the corresponding index for the other two lists.
It is of course possible to have more complicated relationships between the indexes of multiple lists. An example would be where a range of values is needed from the second list, rather than just a single point. These could be accessed using additional if statements within the loop.
However, if the relationship is direct then Python offers a simple alternative: zip.
Exercise: Compare near-identical lists
Two minerals in the same series have very similar formulas. Write a program to compare the two and identify where they differ. Also identify the index at which they differ.
# The formulae of the two minerals
mineral_1 = ["Pb", "Pb", "Cu", "Cr", "O", "O", "O", "O", "As", "O", "O", "O", "O", "O", "H"]
mineral_2 = ["Pb", "Pb", "Cu", "Cr", "O", "O", "O", "O", "P", "O", "O", "O", "O", "O", "H"]
Click to view answer
You can do this by first zipping the two lists to compare element-wise, then appending both elements to another list in order to find the discrepancy. It is pretty obvious when using fairly short lists of strings, but imagine if you were using huge arrays of numbers - it would take a lot longer playing spot-the-difference with that!
# The formula of the two minerals
mineral_1 = ["Pb", "Pb", "Cu", "Cr", "O", "O", "O", "O", "As", "O", "O", "O", "O", "O", "H"]
mineral_2 = ["Pb", "Pb", "Cu", "Cr", "O", "O", "O", "O", "P", "O", "O", "O", "O", "O", "H"]
# Check if the two minerals have the same number of elements
if len(mineral_1) != len(mineral_2):
print("The two minerals do not have the same number of elements and are not identical.")
else:
# Iterate through the first mineral and compare each element with the second mineral
for index, element in enumerate(mineral_1):
# Check if elements at each index are identical
if element != mineral_2[index]:
print(f"{element} does not match {mineral_2[index]} at index {index}")
else:
print(f"These minerals are identical at each index")
This potential solution uses zip(), enumerate, and nested lists. You can see how you can combine lots of aspects of Python to get a more powerful code.
Using zip()#
zip() is a built-in Python function which gives lists a bit more functionality, especially within loops. It is very important you understand its purpose and syntax.
This function takes multiple lists and associates them element-wise. Elements from each list can then be called using individual temporary variables. It allows you to call multiple lists within one for loop.
atom_names = ["H", "He", "Li", "Be"]
atom_masses = [1.008, 4.003, 6.94, 9.012]
for name, mass in zip(atom_names, atom_masses):
print(f"The mass of {name} is {mass}")
The result is:
The mass of H is 1.008
The mass of He is 4.003
The mass of Li is 6.94
The mass of Be is 9.012
nameis the temporary variable for items in the listatom_namesmassis the temporary variable for items in the listatom_masses
Within the for loop, both temporary variables can be called. When the next loop begins, both temporary variables move on to the next item in each list.
Important: all lists being zipped together must be the same length. If they are not, you will almost certainly get an index error. If you want to check the length of a list, you can do that with the function len(list).
Zipping a list and a nested list
You are able to zip both lists and nested lists, as long as the number of nested lists is equal to the length of the other list.
Run the code below.
molecules = ["H2O", "CO2", "HCN"]
masses = [[1.008, 1.008, 12.011] , [12.011, 15.999, 15.999], [1.008, 12.011, 14.007]]
# Assigns the temporary variable name 'mol' to each string in the list 'molecules'
# Assigns the temporary variable name 'mass' to each list within the list 'masses'
for mol, mass in zip(molecules, masses):
# Find molecule total mass
molecular_mass = mass[0] + mass[1] + mass[2]
print(f"Molecular mass of {mol} is {molecular_mass:.3f}")
print("This is the end of the loop.")
You will see that for each string within the list molecules and for each list within the list masses, we can call both items at the same time. For nested lists, we can call entries within the nested lists using square brackets. Remember that due to Python’s zero-indexing, the first item in the nested list has to be called with the number ‘0’ and so on.
Note that the method of calling the index of the nested list would not work if those lists were varying lengths. If you try to call an index larger than the largest index in the list, you would get an index error and Python would not be able to run the code. In that case, you would instead want to use nested loops (discussed later in the lesson).
Zipping more than 2 lists
You can zip more than just two lists together. Copy out and run this code:
atom_names = ["H", "He,", "Li", "Be"]
atom_masses = [1.008, 4.003, 6.94, 9.012]
atom_proton_number = [1, 2, 3, 4]
for name, mass, proton_number in zip(atom_names, atom_masses, atom_proton_number):
print(f"The mass of {name} is {mass}. It has {proton_number} protons.")
Here, we have associated 3 lists element-wise, and are able to refer to all three temporary variables within the loop.
Exercise: Transform coordinates from lists of x and y
A cartesian coordinate, \((x, y)\) can be rotated clockwise to a new position \((x', y')\) by an angle \(\theta\) around the origin using the following formulae:
\( x' = x \ cos(\theta) + y\ sin(\theta) \)
\( y' = -x \ sin(\theta) + y\ cos(\theta) \)
A group of hydrogen atoms have the following x and y coordinates:
x_coords = [1.12, -6.99, -9.48, 8.89, -6.28, -4.98, 6.27, 7.35, 0.87, -9.50]
y_coords = [-1.87, 1.43, 6.38, 4.44, -0.12, -1.56, -8.71, -3.78, -0.31, -1.24]
By zipping together these lists and iterating through with a for loop, rotate these coordinates by 30 \(\degree\) clockwise around the origin. Store the new values in two new lists.
Hint: Remember that you can access sin() and cos() functions using the Python module math, and that these modules take values in radians, not degrees.
This is the answer you should get:
Rotated x coordinates: [0.03, -5.34, -5.02, 9.92, -5.5, -5.09, 1.07, 4.48, 0.6, -8.85]
Rotated y coordinates: [-2.18, 4.73, 10.27, -0.6, 3.04, 1.14, -10.68, -6.95, -0.7, 3.68]
Click to view answer
import math
# Coordinates to be rotated
x_coords = [1.12, -6.99, -9.48, 8.89, -6.28, -4.98, 6.27, 7.35, 0.87, -9.50]
y_coords = [-1.87, 1.43, 6.38, 4.44, -0.12, -1.56, -8.71, -3.78, -0.31, -1.24]
# Define the angle of rotation in radians
angle = math.radians(30) # degrees
# Empty lists to store the rotated coordinates
rot_x_coords = []
rot_y_coords = []
# Loop through the original coordinates, zipping x and y together
for x, y in zip(x_coords, y_coords):
# Rotate the coordinates by the specified angle
new_x = x * math.cos(angle) + y * math.sin(angle)
new_y = -1 * x * math.sin(angle) + y * math.cos(angle)
# Round the new coordinates to 2 decimal places
new_x = round(new_x, 2)
new_y = round(new_y, 2)
# Append the new coordinates to the respective lists
rot_x_coords.append(new_x)
rot_y_coords.append(new_y)
print("Rotated x coordinates: ", rot_x_coords)
print("Rotated y coordinates: ", rot_y_coords)
Here is a summary of notable features:
Converting degrees to radians using the
mathfunctionradians().Define our new lists outside the loop.
Calculate the new values using the rotation formulae.
Round our answer to two decimal places using the Python function
round(). This function takes two arguments: firstly the value, and secondly the number of decimal places you would like to round it to.The transformed coordinates are appended to new lists and printed to the screen.
Iterating using range()#
Instead of using lists we can also iterate using the built-in Python function range(). This allows you to iterate a loop a set number of times, from 0 upwards. We often use i or x as the variable representing the number in the range.
Try running the following piece of code:
for i in range(3):
print(i)
It will print the following:
01
2
You will notice it has printed 0,1,2, but not any further than that. This is again because of Python’s 0-indexing, which has been mentioned previously. In this case, Python will print 3 numbers starting from 0. This means if you want to print up to and including the number 3, you have to put range(4).
The range() function is also able to count from one specified number to another specified number in integer jumps. When we are providing more than one modifier to the function, we must input them in the following way:
for i in range(start, end, jump):
print(i)
This will start counting from and including the start number, up to and not including the end number, in intervals specified by the third number. These can be positive or negative, and Python can count up or down. However, beware, as the range() function cannot take float values, only integer values.
Predicting range() output
Write out and run the following for loops and predict the output. Make sure to look at what number it starts and ends at.
for x in range(2, 10):
print("Count: ", x)
The code above will first print Count: 2, and then print every integer number up to and including Count: 9. It will not print the number 10. This is because of Python’s zero-indexing. It prints 8 numbers overall.
for x in range (10,-1,-1):
print("Count: ", x)
The code above will first print Code: 10, and then count backwards in integer numbers down to Count: 0. It will not print the number -1.
for x in range(3,9,2):
print("Count in twos: ", x)
This code will print the following:
Count in twos: 3Count in twos: 5
Count in twos: 7
It starts at the number 3 and counts upwards in twos. The final number we have given in in the code is the number 9, however because of Python’s zero-indexing, this really corresponds to the number 8, and so the loop stops after only three iterations and does not print ‘Count: 9’.
Creating a list using loops
If you want to quickly create a list containing lots of numbers, you could use a for loop. In the following example, we are creating a list of the values of ml, the magnetic quantum number, for an f orbital. This takes values of -3 to 3.
# ml values of an f orbital
ml_d_orbital = []
for i in range(-3, 4, 1):
ml_d_orbital.append(i)
print(ml_d_orbital)
This prints:
[-3, -2, -1, 0, 1, 2, 3]
This is especially useful when you need a large list of regularly-spaced numbers.
To access floating point numbers, you could either use functions in the library NumPy, or you can simply scale the list and divide i by the required number.
For example, to create a list of numbers from 0 to 2.5 increasing in steps of 1, you could use the following code:
floats = []
for i in range(0,26,1):
floats.append(i/10)
print(floats)
which results in the following list:
[0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5]
Iterating the length of a list using len()
You can use the built-in Python function len() to find the length of a list. You could then iterate a number of times corresponding to the length of the list.
atom_list = ["H","He","Li","Be"]
for index in range(len(atom_list) + 3):
print(index)
print("This is a calculation")
Here, we have a list of strings. I want to do a calculation the same number of times as is in my list, plus three more times. By using len(), I can achieve this.
Note! In this case, the temporary variable index does not refer to items within atom_list, but is an index created from the range() function.
Creating an empty nested list
In a certain projects you may need to create a list of empty coordinates to use later. In this case, the coordinate [0,0,0] repeated 1000 times. Instead of writing it out manually, you can use a for loop. Copy out and run this code. Check the correct number of coordinates are being outputted, then change it to create a list of 1000 empty coordinates.
coordinates = []
for i in range(10):
temp_coord = [0]*3
coordinates.append(temp_coord)
print(coordinates)
The output is:
[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
For the purposes of testing that the program will work, you could only create 10 coordinates and count them by hand. However, you can make this number as large as you want. Change range(10) to range(1000) to see this in action.
If you have not seen the syntax [0]*3, all it means is to create a list comprised of ‘0’ three times. It is a quicker way of creating large lists of identical contents.
Exercise: Correct the error
Try the following for loops. They will return an error, try to figure out why.
for x in range(10,5): print(x)
for x in range(5.5): print(x)
Click to view answer
The first example will not return any output, as it does not know it needs to count down. Add the number -1 separated by a comma to the range() function to correct it.
The second example returns an error, because the range() function cannot handle float numbers. It also cannot jump in floats.
Exercise: List certain elements using ``range``
In the neutral state, elements higher than Lanthanum (atomic number = 57) contain filled or partially-filled f orbitals. Quickly create a list of atomic numbers of all elements (up to and including Nobelium at 102) of elements containing f electrons.
Click to view answer
# Empty list of atomic weights for all elements with f electrons in the neutral state
f_electrons = []
for i in range(58, 103):
f_electrons.append(i)
print(f_electrons)
First we create an empty list. The element Cerium, just after Lanthanum, is the first element with f electrons, so it is the start of the range. Nobelium, with atomic number 102, is the last element, however because of Python’s 0-indexing, we must set the final value as 103, not 102. The output of this is a list of elemental atomic numbers:
[58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102]
Think further: What if we wanted to create a list of elemental symbols from this list of atomic numbers. You could do this through a dictionary storing symbol:number as the key:value pairs.
Nested loops#
You can also have loops inside of loops, just as you can have lists inside of lists. We call this nested loops.
Try this code to help your understanding:
for i in range(4):
print("The start of the outer loop")
for j in range(1,4):
print(j)
The inner code for j in range(1,4): runs three times, counting from 1 to 3 (remember 0-indexing!). It does this the number of times dictated by the outer loop for i in range(4). Every time this outer loop starts, it has been signposted by the print() phrase.
If you are struggling to understand nested loops, have a look at the flowcharts lesson.
Exhaustive multiplication - combining every item with every other item
Below is a piece of code with two lists. Every number in the first list is multiplied by every value in the second, exhausting every possibility. This is one of the easiest ways to visualise a nested for loop.
numbers = [2, 3, 4]
multipliers = [5, 6, 7]
for n in numbers:
for m in multipliers:
ans = n * m
print(f"{n} * {m} = {ans}")
This prints the following:
2 * 5 = 10
2 * 6 = 12
2 * 7 = 14
3 * 5 = 15
3 * 6 = 18
3 * 7 = 21
4 * 5 = 20
4 * 6 = 24
4 * 7 = 28
In this example, we can see that the first value from the list ‘numbers’ is taken, and is used to multiply by each value within ‘multipliers’. Then the loop moves on to the second value in ‘numbers’ and again multiplies by all three values in ‘multipliers’.
Identifying items repeated in two lists
You have two lists and want to make sure than none are repeated from one list to the other. You could use nested lists to check every combination of the two lists.
molecules_1 = ["H2O", "HCN", "CO2", "NH3"]
molecules_2 = ["NO2", "CO2", "CH3", "H2", "HCN"]
for i in molecules_1:
for j in molecules_2:
if i == j:
print(i)
The output would correctly identify that both CO2 and HCN are repeated.
Creating a list of coordinates
Create a list of 2-dimensional coordinates for every point in a grid running from x = 0 to x = 2 and from y = 0 to y = 4.
# Define the lists we will use
x_axis = []
y_axis = []
coord_grid = []
# Create x axis values
for i in range(3):
x_axis.append(i)
# Create y axis values
for j in range(5):
y_axis.append(j)
for x_coord in x_axis:
for y_coord in y_axis:
coord_grid.append([x_coord,y_coord])
print(coord_grid)
The output of this is a 2D grid covering all integer points from x = 0 to x = 2, and y = 0 to y = 4:
[[0, 0], [0, 1], [0, 2], [0, 3], [0, 4], [1, 0], [1, 1], [1, 2], [1, 3], [1, 4], [2, 0], [2, 1], [2, 2], [2, 3], [2, 4]]
Think further: If we wanted to plot these positions, we could do this using the Python module matplotlib.pyplot, which is discussed in a further tutorial.
This example highlights a common issue of loops, repeating the same loop several times. In this example we can do away with the first two loops that generate the x-axis by combining them with the nested loop.
It is always a good idea to check your code for extra loops that could be combined into one. It can often improve the efficiency and neatness of your code.
# Define the list we will use
coord_grid = []
for x_coord in range(3):
# Create x axis value
for y_coord in range(5):
# Create y axis value
coord_grid.append([x_coord,y_coord])
print(coord_grid)
Exercise: Nested loops and nested lists
Write a program that adds up each molecule’s mass from a nested list of their atomic masses, given below.
Since the nested lists are different lengths, you cannot use square brackets to retrieve items as we did in the enumerate() lesson, or you will receive indexing errors. Instead, iterate through the larger list, then through the smaller lists, to obtain your masses.
atom_masses = [[1.008, 1.008, 15.999], [1.008, 1.008], [12.011, 1.008, 1.008, 12.011, 1.008, 1.008], [22.989, 15.999, 1.008], [12.011, 1.008, 1.008, 1.008, 15.999]]
Click to view answer
molecules = ["H2O", "H2", "C2H4", "NaOH", "CH3OH"]
atom_masses = [[1.008, 1.008, 15.999], [1.008, 1.008], [12.011, 1.008, 1.008, 12.011, 1.008, 1.008], [22.989, 15.999, 1.008], [12.011, 1.008, 1.008, 1.008, 15.999]]
# An empty list to store the molecular masses
molecule_masses = []
# Loop through each molecule's atomic masses and calculate the total mass
for molecule in atom_masses:
mass = 0
# Loop through each atom's mass in the molecule
for atom_mass in molecule:
mass += atom_mass
mass = round(mass, 3)
# This occurs after the inner loop, so it gives the total mass of the molecule
molecule_masses.append(mass)
print(f"Molecules: {molecules}")
print(f"Molecular masses / g mol-1: {molecule_masses}")
Let’s talk through this code step by step:
The first thing we do is define an empty list that will hold the masses of each molecule.
The outer loop
for molecule in atom_masses:will loop through each nested list. The temporary variable namemoleculewill first take the value of [1.008, 1.008, 15.999], then on the next loop will become [1.008, 1.008], and so on. Each time this loop moves on to a new list, the value ofmasswill reset to 0.The inner loop
for atom_mass in molecule:will loop through each mass in the list stored by the temporary variablemolecule, each time adding it to the mass variable. This loop is effectively doingmass = 0 + 1.008 + 1.008 + 15.999for the first molecule, and then resetting to 0 to do the same for the second molecule.The mass calculated for each molecule is appended to our empty list of masses in the outer loop (indented only once). If it did this within the inner loop (indented twice), then every single calculation of the mass would be added to the list. Try running it with this line indented twice to see the outcome.
Finally, our molecules and their corresponding masses are printed to the console. If we wanted to print them out item-wise, we could use another
forloop andzip()the two lists together. Another option could be to zip them together in the outer loop and place our print function within there.
The built-in function round() is used here to ensure every mass is given to 3 decimal places. The function takes two arguments (things placed within the brackets of the function): the variable you want to round, and the number of decimal places you want to round it to.
Continue and Break#
One way we can have more control over our loops is by using the Python commands continue and break.
continueis used to end the current iteration of the loop, and continue on to the next iteration.breakis used to end the entire loop.
Run the two sets of code below and compare them.
# Continue molecules = ["H2O", "HCN", "CO2", "NH3", "CH3"] for formula in molecules: if formula == "CO2": continue print(formula) print("This represents the rest of the code continuing")
# Break molecules = ["H2O", "HCN", "CO2", "NH3", "CH3"] for formula in molecules: if formula == "CO2": break print(formula) print("This represents the rest of the code continuing")
In the continue statement, “CO2” is not printed, as the loop has already restarted onto the next iteration, which is to print “NH3”.
In the break statement, nothing is printed from “CO2” onwards, as the entire loop has ended as soon as the condition became True.
Using continue
To retrieve only molecules containing oxygen, this code uses a continue statement.
molecules = ["HCN", "H2O", "NH3", "CO2"]
for mol in molecules:
if "O" not in mol:
continue
print(mol)
The output will be:
H2O
CO2
Notice that we have used the logic operator not to indicate that we are ending the loop for every molecule that does not contain an oxygen.
Using break
To retrieve only the first molecule that contains an oxygen atom, this code uses break.
molecules = ["HCN", "NH3", "H2O", "CO2"]
for mol in molecules:
if "O" in mol:
print(mol)
break
print("This represents the rest of the code")
Notice that in this code, we haven’t used not, and instead placed print() within the if statement. This allows the program to first print ‘H2O’ before breaking out of the loop. It is really important to understand which parts of your code are within which statement, and the order in which they occur. If you are struggling to imagine it, try creating or looking at some flowcharts.
Further Practice#
Question 1#
The workfunction of a metal is the energy required by an incoming photon to remove an electron from its surface. The energy of a photon in J can be calculated using:
\( E = \frac{hc}{\lambda} \)
Where E is energy in Joules, c is the speed of light in vacuum (3.00×108) and \(\lambda\) is wavelength in metres.
A metal has a workfunction of 6.35 eV, which is equal to 1.017×10-18 J. From the following list of wavelengths in nm, print all values which will result in a photon being released. (Hint: you will need to use an if statement)
wavelengths = [276, 59, 0.5, 1183, 52, 0.002, 127, 474] # nm
h = 6.626e-34 # m^2kgs^-1
c = 3.00e8 # ms^-1
workfunction = 1.017e-18 # J
Click to view answer
Remember that there could be multiple ways to approach the same question. Below is one solution.
wavelengths = [276, 59, 0.5, 1183, 52, 0.002, 127, 474] # nm
h = 6.626e-34 # m^2kgs^-1
c = 3.00e8 # ms^-1
workfunction = 1.017e-18 # J
for value in wavelengths:
energy = (h * c) / (value * 1e-9) # J
if energy >= workfunction:
print(value, "nm will release an electron")
else:
print(value, "nm will NOT release an electron")
The output of this code works, but is a bit difficult to read. Perhaps you could try instead appending to a list of wavelengths that do release an electron and a list of wavelengths that do not release an electron.
Question 2#
Some IR data has been retrieved after a lab. Every peak retrieved by the system has been saved as a list of wavelengths (in cm-1) and a separate list of corresponding transmittance.
Any peaks with a wavenumber below 1500 cm-1 belong to the ‘fingerprint zone’, and needs to be discarded. A transmittance of 100% means that all light has passed through the compound, and for this particular lab, any transmittance above 95% can be considered background noise, and be discarded.
Using the following data, print the peak values that should be considered in analysis, and append them to separate lists of shifts and transmittance for use later.
peak_shift = [3542.63, 3497.12, 3077.12, 3002.03, 2930.46, 2925.47, 2919.60, 2740.36, 1730.00, 1715.89, 1710.20, 1705.23, 1700.458, 1599.30, 1434.42, 1109.34, 808.34, 550.02, 428.43] # cm-1
peak_transmittance = [66, 73, 41, 57, 95, 96, 97, 70, 38, 35, 97.2, 95.9, 96.1, 50, 59, 97.8, 99.0, 74, 66.0] # %
Click to view answer
This is the perfect opportunity to use zip(). Do not worry if your code looks a bit different.
# Data retrieved from the lab.
peak_shift = [3542.63, 3497.12, 3077.12, 3002.03, 2930.46, 2925.47, 2919.60, 2740.36, 1730.00, 1715.89, 1710.20, 1705.23, 1700.458, 1599.30, 1434.42, 1109.34, 808.34, 550.02, 428.43] # cm-1
peak_transmittance = [66, 73, 41, 57, 95, 96, 97, 70, 38, 35, 97.2, 95.9, 96.1, 50, 59, 97.8, 99.0, 74, 66.0] # %
# Empty lists of the major peaks we want to keep
maj_peak_shift = []
maj_peak_transmittance = []
# Zip the two data lists together to consider both element-wise
for shift, transmittance in zip(peak_shift, peak_transmittance):
# We will only keep data if its shift is above 1500 AND its transmittance is below 95
if shift > 1500 and transmittance < 95:
print(f"{shift} cm-1, {transmittance} %")
maj_peak_shift.append(shift)
maj_peak_transmittance.append(transmittance)
else:
continue
print(maj_peak_shift)
print(maj_peak_transmittance)
Try out this code. Does the output of this code match yours?
Let’s talk through it step by step.
First we have defined out lists of data, and our empty lists to contain our major peaks.
With a
forloop, we have zipped the two lists together. Values in the listpeak_shiftare given the temporary variable nameshiftand values in the listpeak_transmittanceare given the temporary variable nametransmittance.With an
ifstatement, we are checking that two comparisons are both true using the logical operatorand. For each loop, we must ensure that the shift is greater than 1500 AND the transmittance is less than 95.The lines of indented code just after the
ifstatement will only run if both comparisons above are True. The peak wavenumber and its transmittance are printed, and both numbers appended to separate lists.The
else:statement contains the indented wordcontinueand nothing else. If either of the comparisons in theifstatement are False, the code will simply continue onto the next loop.
Learning Objectives#
Use a
forloopIterate through a loop and using
range()
Summary#
The syntax of a
forloop is:for i in list:, and the code within the loop is on the lines immediately following it, indented using<tab>If retrieving a value in a nested list, you can use
i[0]to retrieve the first item,i[1]to retrieve the second item, and so on. It is not recommended to use Python lists for anything higher than a 2-dimensional list, as it gets confusing very quickly. Instead, you might want to consider NumPy arrays (covered in a further lesson).When iterating through two related lists of the same size, you can associate them pairwise using the
zip()function. The syntax is:for i, j in zip(list1, list2):. The two lists MUST be the same dimensions.You can assign each item in a list an index value using the
enumerate()function. The syntax is:for index, value in enumerate(list):.Use the
range()function to iterate a set number of times. The syntax is:for i in range(2,8,1)), where ‘2’ is the start number, ‘8’ is the end number (which, if printed, would be ‘7’ because of Python’s zero-indexing), and ‘1’ is the increase step (which can be negative if you are counting down). If you only put a single number into therange()function, it will count from 0 up to but not including the number you have put in.The built-in function
len()will find the length of a list.Loops inside of loops can compare every combination, iterate multiple actions on each element in a list, and more.
continuewill move the program onto the next iteration of the loop.breakwill end the loop entirely