Python is a versatile language, widely used for urban analysis and data science. This guide introduces its foundational concepts.
The best way to learn Python is by trying things out. Change the examples, break them, and see what happens. Progress comes fastest when you actively use the tools to solve problems and learn as you go.
This is a brief introduction to Python. To use it effectively day-to-day, you’ll need hands-on practice and to explore further resources, which are widely available online. It’s a great time to learn, as Large Language Models (LLMs) can help speed up the process and answer many of your questions. However, be mindful: use LLMs to amplify your understanding and guide your learning, not as a substitute for grasping the concepts yourself. Relying on them to do the work without understanding means you might not learn effectively and could miss out on using these tools to their full potential.
Comments
Use # to add comments, which Python ignores. Comments help explain your code.
# This is a commentprint("Hello, World!") # Print a greeting
Hello, World!
Multiline comments use triple quotes:
"""This is a multiline comment.It can span multiple lines."""
Variables
Variables act as labelled containers for data. Assign values using =.
# Assigning values to variablesmessage ="Hello, Python!"# Text (string)count =10# Whole number (integer)price =19.99# Decimal number (float)is_active =False# True/False value (boolean)# Accessing variable valuesprint(message)print(count)print(price)print(is_active)
Hello, Python!
10
19.99
False
Data Types
Variables can hold different types of data, such as numbers (integers or floats) and text (strings). The functionality available often depends on the data type, though some behaviours are shared across types.
str (String): Text, enclosed in quotes (" or '). Example: "Urbanism".
int (Integer): Whole numbers. Example: 42, -7.
float (Float): Numbers with decimals. Example: 3.14, -0.001.
bool (Boolean): Logical values, True or False (capitalised).
A method is like a function that’s associated with a particular object or type (like a string). For example, str.upper() is a method of the str class that converts a string to uppercase. You access methods using dot notation (object.method()) and execute them by using parentheses. If a method requires options or parameters, they go inside the parentheses.
Common built-in methods include converting text to lower or upper case, or removing leading/trailing whitespace and characters.
F-strings (formatted string literals) offer a convenient way to embed expressions inside string literals for formatting. Simply prefix the string with an f and write expressions in {}.
city ="Berlin"population =3800000info =f"The city of {city} has a population of {population}."print(info)
The city of Berlin has a population of 3800000.
Exercises
Create a variable for a number. Print its type using type().
Create a variable for your name, then print a statement using an F-string to say “Hello, [your name]!”.
Perform all basic arithmetic operations (+, -, *, /, %) on two numbers and print the results. Experiment with parentheses () to change the order of operations.
Collections
Often, you need to work with multiple pieces of data at once. Python provides several ways to group data, each with its own characteristics.
Type
Ordered
Unique
Mutable
Example Syntax
Example Use Case
List
Yes
No
Yes
[1, 2, 3]
Shopping list, coordinates
Tuple
Yes
No
No
(1, 2, 3)
Fixed coordinates, RGB colour
Set
No
Yes (Values)
Yes
{1, 2, 3}
Unique tags, deduplication
Dictionary
Yes
Yes (Keys)
Yes
{"a": 1, "b": 2}
Lookup tables, data records
Lists
Use lists when the order of items is important and you might need to change them later. Lists are mutable (meaning they can be changed after creation). Think of them like a row of mailboxes: you can change what’s inside a mailbox. Access items using their index (position, starting from 0) in square brackets []. Negative indexing is also handy, where -1 refers to the last item, -2 to the second last, and so on.
Python has several useful built-in functions. One is len(), which we’ll use below. It returns the length (i.e., the number of items) of an object like a list or a string.
Get the number of items
len(planets) # Output: 5
5
Lists are mutable (can be changed after creation)
planets[0] ="Fast Planet"# Change the first itemplanets[3] ="Red Planet"# Update Marsplanets
Use dictionaries when you need to associate values with unique keys (like looking up a word in a dictionary to find its definition). They are very efficient for retrieving values when you know the key.
Once data is stored, you can easily retrieve or update a value using its key.
Dictionaries are defined using curly braces {}. Each item is a key-value pair, separated by a colon :. The key is on the left, and the value is on the right.
Similar to lists, dictionaries use square brackets [] to access values, but instead of a numerical index, you use the key.
# Create a dictionary using curly braces {}# Format: {key1: value1, key2: value2}building_info = {"type": "Residential","floors": 5,"year_built": 1998}building_info
# Common dictionary operationsprint(building_info.keys()) # Get all keysprint(building_info.values()) # Get all valuesprint(building_info.get("floors")) # Get valueprint(building_info.get("address")) # Returns None
Use tuples for ordered collections of items that shouldn’t change after creation (e.g., coordinates). Tuples are immutable, meaning they cannot be altered once created.
# A tuple of coordinatespoint = (10.5, 25.3)print(point)print(point[0]) # Access items like lists
(10.5, 25.3)
10.5
# This would cause a TypeError! Tuples are immutable.point[0] =11.0
Tuples are often used to return multiple values from functions or for unpacking (we will explain functions later).
def get_coordinates():return (10.5, 25.3)
This is a common pattern in Python, especially when dealing with functions that return multiple values. You can unpack the tuple into separate variables.
lat, lon = get_coordinates()print(f"Latitude: {lat}, Longitude: {lon}")
Latitude: 10.5, Longitude: 25.3
The below would raise an error because the tuple only has two items, but we are trying to unpack it into three variables:
lat, lon, z = get_coordinates()
Sets
Use sets when you need a collection of unique items, and the order of those items isn’t important.
# Duplicate "London" is ignoredunique_cities = {"London", "Paris", "Berlin", "London"}# Output might be in any order, e.g., {'Berlin', 'London', 'Paris'}print(unique_cities)
{'London', 'Paris', 'Berlin'}
# Check for membershipprint("Paris"in unique_cities) # True
True
# Add an itemunique_cities.add("Rome")print(unique_cities)
{'London', 'Paris', 'Berlin', 'Rome'}
# Set operationsset1 = {1, 2, 3}set2 = {3, 4, 5}print(set1.union(set2)) # All items from both: {1, 2, 3, 4, 5}print(set1.intersection(set2)) # Items in both: {3}print(set1.difference(set2)) # Items in set1 but not set2: {1, 2}
{1, 2, 3, 4, 5}
{3}
{1, 2}
Control Flow
Control flow structures are fundamental to programming. They allow your code to make decisions (using if/elif/else) and repeat actions (using for/while loops).
Conditionals
Conditionals allow your program to execute different blocks of code based on whether certain conditions are True or False. They use comparison operators to evaluate conditions. This conditional logic enables your program to adapt its behaviour to various situations.
# Comparison operators return boolean valuesprint(5==5) # Equal to: Trueprint(5!=3) # Not equal to: Trueprint(5>3) # Greater than: Trueprint(5<3) # Less than: Falseprint(5>=5) # Greater than or equal to: Trueprint(5<=3) # Less than or equal to: False
True
True
True
False
True
False
Indentation
Indentation is non-negotiable in Python: it defines code blocks. Always use consistent spacing (the common convention is 4 spaces per indentation level) and avoid mixing spaces with tabs. Code blocks are introduced by a colon (:) and can be nested.
# FYI - we will explain loops nextfor i inrange(3):# Example of indentationif i ==2:# Example of nested indentationprint(f"{i} is two")else:print(f"{i} is not two")
0 is not two
1 is not two
2 is two
Use if, elif (short for ‘else if’), and else to direct the flow of execution.
population_density =5000# people per sq kmif population_density >10000:print("Very high density")elif population_density >3000:print("High density")elif population_density >1000:print("Medium density")else:print("Low density")
High density
Conditions can be combined using and and or.
# Combining conditions with 'and', 'or'floors =12if population_density >3000and floors >10:print("High density and tall buildings")
High density and tall buildings
For identity checks (e.g., if a value is None), use is and is not.
# Checking for None (often used for missing data)maybe_data =None# Represents absence of a valueif maybe_data isNone:print("Data is missing.")else:print(f"Data found: {maybe_data}")
Data is missing.
For checking if a value is in a collection (like a list or set), use in and not in.
# Check if a value is in a listcities = ["London", "Paris", "Berlin"]if"Paris"in cities:print("Paris is in the list of cities.")if"Rome"notin cities:print("Rome is not in the list of cities.")
Paris is in the list of cities.
Rome is not in the list of cities.
Loops
Loops are fantastic for avoiding repetitive code. They are essential for processing items in collections and can be combined with conditionals to apply logic selectively to each item.
for Loops
Use for loops to iterate over each item in a sequence (such as a list, tuple, or string). In each pass (iteration), the loop variable (e.g., planet in the example below) takes the value of the current item from the sequence.
# Loop through a listfor planet in planets:print(f"Checking planet: {planet}")
Checking planet: Fast Planet
Checking planet: New Planet
Checking planet: Earth
Checking planet: Red Planet
Checking planet: Jupiter
while Loops
Use while loops when you don’t know exactly how many times to loop in advance, but you know the condition under which the loop should continue running.
Caution: Ensure the while loop’s condition eventually becomes False, otherwise it will run forever (an infinite loop)!
Exercise
Create a list named numbers containing the integers from 1 to 10. Then, write a loop that iterates through this list. For each number, if it is even, print the number.
Hint: The modulo operator (%) gives the remainder of a division. A number is even if number % 2 == 0.
Functions
Functions help you organise your code into logical, reusable blocks. This improves readability, makes debugging easier, and simplifies maintenance. If you find yourself copying and pasting code, that’s often a good sign you could write a function instead.
# Define a function with parameters (inputs)def calculate_density(population, area, unit="sq km"):"""Calculates population density. It's good practice to include a *docstring* (documentation string) right after the def line to explain what the function does, its parameters, and what it returns. Args: population (int): The total population. area (float): The total area. unit (str, optional): The unit for the area. Defaults to "sq km". Returns: float: The calculated density, or 0 if area is non-positive. """if area <=0:print("Area must be positive to calculate density.")return0# Return 0 or raise an error for invalid input density = population / areareturn density
Parameters: These are the names listed in the function definition’s parentheses (e.g., population, area).
Arguments: These are the actual values you pass to the function when you call it.
Default Arguments: You can give parameters default values (e.g., unit="sq km"). If you don’t provide an argument for that parameter when calling the function, the default value is used.
Scope: Variables defined inside a function are typically local to that function, meaning they only exist and can be accessed within that function.
Docstrings: As mentioned, the optional triple-quoted string """...""" right after the def line. They are used to document what the function does, its arguments (often listed under an Args: section), and what it returns (under a Returns: section).
Return Statement: The return statement is used to send a value back from the function to the place where it was called. If a function doesn’t have a return statement, or if the return statement is used without a value, it implicitly returns None.
Call the function with arguments (values for the parameters)
density1 = calculate_density(1000000, 50)print(f"Density 1: {density1:.2f} people per sq km")
Density 1: 20000.00 people per sq km
Calling the function with explicit named arguments allows you to specify which parameter each value corresponds to, making your code clearer and more readable. This is especially useful when a function has many parameters or some have default values.
# Can name argumentsdensity2 = calculate_density( population=500000, area=100, unit="square kilometres")print(f"Density 2: {density2:.2f} people per square kilometres")
Density 2: 5000.00 people per square kilometres
While functions don’t always have to return a value (e.g., a function that just prints something), they often do.
Importing Modules
Python has a vast collection of modules that provide ready-to-use tools – so you often don’t need to reinvent the wheel! If you encounter an unfamiliar function, it’s a good idea to check the import statements, usually found at the top of the file, as the function might come from an imported module.
import module_name makes the functions and variables within that module available, accessed via module_name.function_name.
import math # Import the built-in math moduleradius =5area = math.pi * (radius **2) # Use math.piprint(f"Circle Area: {area:.2f}")
# You can also import specific methods from a modulefrom math import sqrt # Import only the square root functionprint(f"Square root of 16 is {sqrt(16)}")
Square root of 16 is 4.0
In later sections, we’ll be making extensive use of powerful libraries such as geopandas and numpy, which are imported as modules.
Common Errors
Errors are a normal part of programming! Learning to read and understand error messages is a key skill for fixing them. If you get stuck, searching for the error message online is often helpful – chances are, someone else has encountered the same problem. You can also ask an LLM to help explain what an error message means.
NameError: name '...' is not defined: You tried to use a variable before assigning a value to it, or you misspelled the variable name.
TypeError: unsupported operand type(s) for ...: You tried to perform an operation on incompatible data types (e.g., adding a string to an integer: "Hello" + 5).
SyntaxError: invalid syntax: You made a mistake in the Python grammar (e.g., missing colon :, mismatched parentheses ()).
IndexError: list index out of range: You tried to access an item in a list using an index that doesn’t exist (e.g., accessing my_list[5] when the list only has 3 items).
ImportError: No module named '...' or ModuleNotFoundError: No module named '...': You tried to import a module that Python can’t find. It might be misspelled or not installed.
Debugging Tip: When you encounter an error, the last line of the message usually tells you the specific type of error. The lines mentioned in the traceback help you find where the problem occurred in your code. Sprinkling print() statements in your code to check the values of variables at different stages can also be a very effective way to debug. And remember, it’s common to fix one error only to find another – just tackle them one by one.
Comments
Use
#
to add comments, which Python ignores. Comments help explain your code.Multiline comments use triple quotes: