Iterating over collections
Iterating over collections
Iterating over a collection type, be it a list, tuple, or dictionary, is a common operation in programming. Python provides several ways to iterate over these collections, allowing you to access each item in the collection one at a time.
Lists & Tuples
Concept: Iterating over lists or tuples
To iterate over a list or tuple, you can use a for .. in loop. The loop will go through each item in the collection one by one.
users = ["Alice", "Bob", "Charlie", "David"]
for user in users:
print(user, end=" ") # Output: Alice Bob Charlie Davidusers = ("Alice", "Bob", "Charlie", "David")
for user in users:
print(user, end=" ") # Output: Alice Bob Charlie DavidAs the example above shows, the syntax for iterating over a list or tuple is the same. If desired, you can also use the enumerate() function to get both the index and the value of each item in the collection. This function return s a tuple containing the index and the value of each item in the collection, which can be unpacked in the for .. in loop.
users = ["Alice", "Bob", "Charlie", "David"]
for (i, user) in enumerate(users):
print(f"({i}) {user}", end=" ") # Output: (0) Alice (1) Bob (2) Charlie (3) Davidusers = ("Alice", "Bob", "Charlie", "David")
for (i, user) in enumerate(users):
print(f"({i}) {user}", end=" ") # Output: (0) Alice (1) Bob (2) Charlie (3) DavidWhile the for .. in loop is the most common way to iterate over a collection, you can also use a for loop in combination with the range() function to achieve the same result.
users = ["Alice", "Bob", "Charlie", "David"]
for i in range(len(users)):
print(users[i], end=" ") # Output: Alice Bob Charlie DavidFinally, you can also use a while loop in combination with an index variable to achieve the same result.
users = ["Alice", "Bob", "Charlie", "David"]
i = 0
while i < len(users):
print(users[i], end=" ") # Output: Alice Bob Charlie David
i += 1Both the for loop with range() and the while loop with an index variable can be used to iterate over a collection, but they are less readable and more prone to errors than using a for .. in loop. There are reasons to use these methods, but they are generally not recommended unless you have a specific need to do so and those needs are not applicable to system or network administration tasks.
Dictionaries
Concept: Iterating over dictionaries
Iterating over a dictionary can be done using a for .. in loop. By default, the loop will iterate over the keys of the dictionary. However, you can also iterate over the values or key-value pairs using the values() and items() methods, respectively.
config = {
"hostname": "srv01.example.com",
"ip_address": "192.168.1.10",
"services": ["ssh", "http", "ssh"]
}
for key in config:
print(key, end=" ") # Output: hostname ip_address servicesconfig = {
"hostname": "srv01.example.com",
"ip_address": "192.168.1.10",
"services": ["ssh", "http", "ssh"]
}
for value in config.values():
print(value, end=" ") # Output: srv01.example.com 192.168.1.10 ['ssh', 'http', 'ssh']config = {
"hostname": "srv01.example.com",
"ip_address": "192.168.1.10",
"services": ["ssh", "http", "ssh"]
}
for key, value in config.items():
print(f"{key}: {value}", end=" ") # Output: hostname: srv01.example.com ip_address: 192.168.1.10 services: ['ssh', 'http', 'ssh']Comprehensions
A for loop works fine if you want to iterate over a collection and do something with each item, but if you want to create a new collection based on the items in a collection, you can use a comprehension.
Concept: Comprehension
Comprehensions are a concise way to create new collections (lists, sets[1], or dictionaries) by applying an expression to each item in an existing collection. They allow you to generate a new collection in a single line of code, making your code more readable and concise.
# Create a new list of squares from an existing list of numbers
numbers = [1, 2, 3, 4, 5]
squares = [x ** 2 for x in numbers]
# Create a new list of the squares of even numbers from an existing list of numbers
even_squares = [x ** 2 for x in numbers if x % 2 == 0]
# Create a new dictionary of squares from an existing list of numbers
squares_dict = {x: x ** 2 for x in numbers}Comprehensions exist for lists and dictionaries, but not for tuples. It is, however, possible to create a tuple using comprehension syntax, but the result will be a generator object, which can be converted to a tuple using the tuple() function.
# Create a new tuple of squares from an existing list of numbers
numbers = [1, 2, 3, 4, 5]
squares_gen = (x ** 2 for x in numbers) # This creates a generator object
squares_tuple = tuple(squares_gen) # Convert the generator to a tuple
# Combine the creation of the generator and the conversion to a tuple in one line
squares_tuple = tuple(x ** 2 for x in numbers)List comprehension
Consider the following configuration file for the ssh service:
sshd_config = [
"# This is the sshd server system-wide configuration file.",
"# See sshd_config(5) for more information.",
"",
"Port 22",
"#AddressFamily any",
"ListenAddress 0.0.0.0",
"",
"# Authentication:",
"PermitRootLogin no",
"MaxAuthTries 3",
"#MaxSessions 10",
"",
"PubkeyAuthentication yes",
"PasswordAuthentication no",
" PermitEmptyPasswords no",
"",
"X11Forwarding yes",
"PrintMotd no",
"#Banner none",
"",
"Subsystem sftp /usr/lib/openssh/sftp-server",
]We want to create a new list that contains only the lines that are not commented out and are relevant, i.e. not empty. Using a classic for loop, we can do this as follows:
directives = []
for line in sshd_config:
line = line.strip()
if line == "" or line.startswith("#"):
continue
directives.append(line)While this works, it is a bit verbose, a more concise way to achieve the same result is to use a list comprehension:
sshd_config = [
"# This is the sshd server system-wide configuration file.",
"# See sshd_config(5) for more information.",
"",
"Port 22",
"#AddressFamily any",
"ListenAddress 0.0.0.0",
"",
"# Authentication:",
"PermitRootLogin no",
"MaxAuthTries 3",
"#MaxSessions 10",
"",
"PubkeyAuthentication yes",
"PasswordAuthentication no",
" PermitEmptyPasswords no",
"",
"X11Forwarding yes",
"PrintMotd no",
"#Banner none",
"",
"Subsystem sftp /usr/lib/openssh/sftp-server",
]
directives = [line.strip() for line in sshd_config if line.strip() != "" and not line.strip().startswith("#")]Dictionary comprehension
Consider a /24 network in which we want to retrieve the hostnames for each IP address. While it is possible to use a list to store the hostnames, it is more appropriate to use a dictionary, where the IP address is the key and the hostname is the value.
We could of course use a for loop to iterate over the IP addresses and retrieve the hostnames:
ip_addresses = list(range(1,255))
hostnames = {}
for host in ip_addresses:
hostnames[host] = get_hostname(f"192.168.1.{host}")Just like with lists, we can use a dictionary comprehension to achieve the same result in a more concise way:
ip_addresses = list(range(1,255))
hostnames = {host: get_hostname(f"192.168.1.{host}") for host in ip_addresses}We do not cover sets in this course, they are a collection type that is similar to lists and dictionaries. Where they differ is that they are unordered and do not allow duplicate values. You can read more about sets in the official Python documentation. ↩︎