Python: Data Structures and Comprehensions
Once variables, conditions, loops, and functions make sense, the next step is learning how to organize data. Python's built-in collections cover most everyday jobs without needing an extra library.
Lists
A list is ordered and changeable. It is a good choice when the order matters or when items will be added and removed.
devices = ["gateway", "field node", "sensor"]
devices.append("test node")
devices[0] = "Pi gateway"
for number, device in enumerate(devices, start=1):
print(f"{number}. {device}")
print(devices[1:3]) # A slice: items at positions 1 and 2
Tuples
A tuple is an ordered collection that should not change after it is created. Tuples are useful for fixed values such as coordinates or settings.
location = (50.4452, -104.6189)
latitude, longitude = location
print(f"Latitude: {latitude}")
print(f"Longitude: {longitude}")
Sets
A set stores unique values. It is useful for removing duplicates or testing whether a value has already been seen.
seen_nodes = {"node-a", "node-b", "node-a"}
print(seen_nodes) # node-a appears only once
if "node-c" not in seen_nodes:
print("This node has not reported yet")
Dictionaries
A dictionary maps keys to values. This is often the best way to represent one named object or a record with several fields.
node = {
"name": "field-node-01",
"battery": 87,
"temperature": 18.4,
"online": True,
}
print(node["name"])
print(node.get("signal", "unknown"))
for key, value in node.items():
print(f"{key}: {value}")
List Comprehensions
A comprehension creates a collection from another iterable. Use one when the expression remains easy to read; an ordinary loop is better when the logic becomes complicated.
readings = [12.5, 18.0, 7.25, 21.5, 16.0] # Keep only readings above 15 warm_readings = [value for value in readings if value > 15] # Transform every value fahrenheit = [value * 9 / 5 + 32 for value in readings] print(warm_readings) print(fahrenheit)
Choosing a Collection
| Collection | Use it when |
|---|---|
list | You need an ordered, changeable sequence |
tuple | You need fixed grouped values |
set | You need unique values or fast membership checks |
dict | You need named keys mapped to values |
Records and Nested Data
Real data is often a list of dictionaries: one dictionary for each node, reading, or device.
nodes = [
{"name": "field-01", "battery": 92, "online": True},
{"name": "field-02", "battery": 41, "online": True},
{"name": "field-03", "battery": 8, "online": False},
]
for node in sorted(nodes, key=lambda item: item["battery"]):
state = "online" if node["online"] else "offline"
print(f"{node['name']}: {node['battery']}% ({state})")
low_battery = [
node["name"] for node in nodes
if node["battery"] < 20
]
print("Check:", ", ".join(low_battery))
Use a dictionary when a field has a name. A tuple such as (92, True) is smaller but makes the code harder to understand later.
Copies, Mutation, and Defaults
original = ["radio", "antenna"]
same_list = original # both names point to one list
copied_list = original.copy()
same_list.append("battery")
print(original) # changed too
print(copied_list) # unchanged
Unexpected changes often come from two variables referring to the same mutable list or dictionary. Make an explicit copy when that is not what you want.
Practice: Summarize Readings
readings = [
{"node": "field-01", "temperature": 18.2},
{"node": "field-02", "temperature": 21.7},
{"node": "field-01", "temperature": 18.9},
]
by_node = {}
for reading in readings:
name = reading["node"]
by_node.setdefault(name, []).append(reading["temperature"])
for name, values in by_node.items():
average = sum(values) / len(values)
print(f"{name}: {average:.1f} C")
dispelled