Part 1 held one city in a handful of variables. A real dataset has thousands of cities, so Python needs containers that hold many values. There are four, and each one suits a different job.
What are collections?
| Collection | Written as | Ordered | Can change | Duplicates | GIS use |
|---|---|---|---|---|---|
| list | [a, b] | yes | yes | allowed | a column of city names |
| tuple | (a, b) | yes | no | allowed | one coordinate pair |
| set | {a, b} | no | yes | removed | unique road classes |
| dictionary | {"k": v} | yes | yes | keys are unique | one feature, field name to value |
1. Lists
A list holds items in order, inside square brackets.
# A list holds items in order, written inside square brackets
cities = ["Bengaluru", "Mysuru", "Mangaluru", "Hubballi", "Chennai"]
print(cities)
print(len(cities)) # how many items
print(cities[0]) # the FIRST item. Counting starts at 0, not 1.
print(cities[-1]) # a minus sign counts from the end, so -1 is the last item
print(cities[1:3]) # a slice: from position 1 up to (but not including) position 3
['Bengaluru', 'Mysuru', 'Mangaluru', 'Hubballi', 'Chennai']
5
Bengaluru
Chennai
['Mysuru', 'Mangaluru']
Position numbers are called indexes and start at 0. A negative index counts from the end, so -1 is the last item. A slice cities[1:3] takes positions 1 and 2 and stops before 3.
Lists can change:
cities.append("Pune") # add to the end
cities.remove("Hubballi") # take this value out
cities.insert(0, "Delhi") # put "Delhi" at position 0, the very start
cities.sort() # sort A to Z (this changes the list itself)
print(cities)
['Bengaluru', 'Chennai', 'Delhi', 'Mangaluru', 'Mysuru', 'Pune']
append adds at the end, insert(0, ...) adds at a position, remove deletes by value and sort puts the list in order. sort() changes the list itself. sorted(cities) gives a new sorted list and leaves the original alone.
Check membership with in:
# "in" checks whether something is in the list and answers True or False
print("Pune" in cities)
print("Mumbai" in cities)
True
False
2. Tuples
A tuple looks like a list with round brackets, but it cannot be changed after it is made. That suits a coordinate, which should not be edited by accident.
# A tuple is like a list but cannot be changed. It uses round brackets and suits things like a coordinate pair.
bengaluru = (12.9716, 77.5946)
print(bengaluru[0]) # the first value (latitude)
print(type(bengaluru))
12.9716
<class 'tuple'>
Use a list when the contents will grow or change, and a tuple for a fixed group of values such as (latitude, longitude). Trying bengaluru[0] = 13 raises a TypeError.
3. Unpacking
Unpacking splits a tuple or list into separate variables in one line.
# Unpacking: put each value of the tuple into its own variable in one line
lat, lon = bengaluru
print(lat)
print(lon)
# *others collects "all the rest" into a list
first, *others = cities
print(first)
print(others)
12.9716
77.5946
Bengaluru
['Chennai', 'Delhi', 'Mangaluru', 'Mysuru', 'Pune']
The names on the left take the values on the right in order. A name with * collects whatever is left into a list.
4. Looping over a list
A for loop repeats a block of indented lines once per item. Part 3 explains loops fully, and here is the pattern for pairs of values.
names = ["Bengaluru", "Mysuru", "Chennai"]
coords = [(12.9716, 77.5946), (12.2958, 76.6394), (13.0827, 80.2707)]
# zip pairs each name with its coordinates. (lat, lon) unpacks each pair straight away.
for name, (lat, lon) in zip(names, coords):
print(f"{name}: {lat}, {lon}") # the f before the quote lets {...} insert a variable's value
Bengaluru: 12.9716, 77.5946
Mysuru: 12.2958, 76.6394
Chennai: 13.0827, 80.2707
zip pairs the two lists item by item, and the tuple on the left unpacks each coordinate.
5. Sets
A set keeps each value once and has no order. It answers questions like which road classes exist.
road_classes = ["NH", "SH", "NH", "MDR", "SH", "NH", "village"]
# A set keeps each value only once, so it removes duplicates. Its order is not fixed.
unique_classes = set(road_classes)
print(unique_classes == {"NH", "SH", "MDR", "village"})
print(len(road_classes), len(unique_classes)) # 7 values, but only 4 different ones
print(sorted(unique_classes)) # sorted() gives a predictable order
True
7 4
['MDR', 'NH', 'SH', 'village']
The list has 7 entries and 4 distinct classes. Printing a set directly can show the items in any order, so use sorted() when you want a stable result.
Sets compare two collections, which is useful when two datasets should cover the same places:
survey_a = {"Bengaluru", "Mysuru", "Chennai"}
survey_b = {"Mysuru", "Chennai", "Pune"}
print(sorted(survey_a & survey_b)) # & = in BOTH (intersection)
print(sorted(survey_a | survey_b)) # | = in EITHER (union)
print(sorted(survey_a - survey_b)) # - = in A but not in B (difference)
['Chennai', 'Mysuru']
['Bengaluru', 'Chennai', 'Mysuru', 'Pune']
['Bengaluru']
& is the intersection (in both), | the union (in either), and - the difference (in the first but not the second). Bengaluru is in survey A only, so a check like this finds missing records.
6. Dictionaries
A dictionary stores pairs: a key (the name) and a value. This is how a GIS feature looks in code, with field names as keys.
# A dictionary stores labelled values: key -> value. Think of one feature with its attributes.
feature = {
"city": "Bengaluru",
"state": "Karnataka",
"population": 8443675,
"lat": 12.9716,
"lon": 77.5946,
}
print(feature["city"]) # look up by key
print(feature.get("elevation")) # .get() is safe: a missing key gives None instead of an error
print(feature.get("elevation", "unknown")) # ...or a default value you choose
Bengaluru
None
unknown
feature["city"] looks up a value by key. A missing key with square brackets raises a KeyError, while .get() returns None or a default you choose. Use .get() when a field might not exist.
Change, add and remove fields:
feature["population"] = 8444000 # change an existing value
feature["elevation_m"] = 920 # add a new key
del feature["state"] # delete a key
print(feature)
{'city': 'Bengaluru', 'population': 8444000, 'lat': 12.9716, 'lon': 77.5946, 'elevation_m': 920}
Loop over keys and values with .items():
# .items() gives each key and its value, so you can print every field
for field, value in feature.items():
print(field, "=", value)
city = Bengaluru
population = 8444000
lat = 12.9716
lon = 77.5946
elevation_m = 920
.keys() gives the field names and .values() the values.
7. A list of dictionaries is a table
Put several dictionaries in a list and you have the rows of an attribute table. Part 7 turns exactly this structure into a pandas DataFrame.
# A list of dictionaries: this is how tables look in plain Python, one dictionary per row
places = [
{"city": "Bengaluru", "state": "Karnataka", "population": 8443675},
{"city": "Mysuru", "state": "Karnataka", "population": 920550},
{"city": "Chennai", "state": "Tamil Nadu", "population": 4646732},
]
for row in places:
print(row["city"], row["population"])
# Build a lookup: city name -> population
populations = {row["city"]: row["population"] for row in places}
print(populations["Chennai"])
Bengaluru 8443675
Mysuru 920550
Chennai 4646732
4646732
The last two lines build a dictionary from the list by looping once. That compact form is a dictionary comprehension, covered in Part 3.
Run this part as a notebook
The code on this page is also a Jupyter notebook. It has the theory in text cells, then the code, then guided practice, an independent assignment and hidden solutions, so you can try each task before you look. Download the Part 2 notebook, open it in JupyterLab and run the cells from top to bottom. Part 1 shows how to install and start Jupyter.
Practice
- Make a list of five cities. Print the first, the last and the middle three.
- Make a list of road classes with duplicates and count how many are unique.
- Make two sets of state names from two imaginary datasets and find the states that are only in one.
- Make a dictionary for your own city with five fields, then add an
elevation_mfield.
Common mistakes
- Using an index that does not exist, such as
cities[10], which raises anIndexError. - Forgetting that a slice stops before the last number.
- Trying to change a tuple.
- Expecting a set to keep order or to hold a list inside it.
- Using
feature["elevation"]when the key may be missing. Use.get(). - Copying a list with
b = a. Both names point to the same list. Useb = a.copy().
Key takeaways
Next: Decisions and loops.

