Difficulty: Intermediate
Lists are one of the most versatile and commonly used data structures in Python. A list is an ordered, mutable collection of items that can hold elements of any data type, including other lists. You create a list by enclosing comma-separated values inside square brackets [], or by using the list() constructor. Unlike arrays in languages like C or Java, Python lists can contain mixed types in the same list, though in practice you usually store homogeneous data.
Indexing allows you to access individual elements of a list by their position. Python uses zero-based indexing, meaning the first element is at index 0, the second at index 1, and so on. If you try to access an index that does not exist, Python raises an IndexError. You can also assign to a specific index to change the value at that position, since lists are mutable.
Negative indexing is a powerful Python feature that lets you access elements from the end of a list. Index -1 refers to the last element, -2 to the second-to-last, and so on. This is extremely useful when you need the last few elements without knowing the list's length. Negative indexing works because Python internally translates a negative index i to len(list) + i.
The len() function returns the number of elements in a list. It runs in O(1) constant time because Python internally tracks the length of every list object, so it does not need to count elements each time. You will use len() constantly when working with lists, especially in loops, conditional checks, and slicing.
Nested lists are lists that contain other lists as elements, effectively creating multi-dimensional data structures. A list of lists can represent a matrix, a grid, a table, or any hierarchical data. You access elements in nested lists using chained indexing: matrix[row][col]. Be careful with how you create nested lists, as multiplying a list with the * operator creates shallow copies that all point to the same inner list.
# Different ways to create lists
empty = []
numbers = [1, 2, 3, 4, 5]
mixed = [1, "hello", 3.14, True, None]
from_range = list(range(5))
from_string = list("Python")
print(empty)
print(numbers)
print(mixed)
print(from_range)
print(from_string)
Lists can be created with literal brackets, from iterables using list(), or from range objects. The list() constructor converts any iterable (string, range, tuple, etc.) into a list.
fruits = ["apple", "banana", "cherry", "date", "elderberry"]
# Positive indexing (0-based)
print(fruits[0])
print(fruits[2])
# Negative indexing (from the end)
print(fruits[-1])
print(fruits[-3])
# Modifying elements via index
fruits[1] = "blueberry"
print(fruits)
Index 0 is the first element, index -1 is the last. Since lists are mutable, you can reassign any element by its index. Attempting to access an out-of-range index raises an IndexError.
colors = ["red", "green", "blue", "yellow"]
print(len(colors))
print("green" in colors)
print("purple" in colors)
print("purple" not in colors)
len() returns the count of elements in O(1) time. The 'in' and 'not in' operators check for membership, scanning the list in O(n) time.
# 2D matrix as nested lists
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
print(matrix[0])
print(matrix[1][2])
print(matrix[-1][-1])
# Iterating over a nested list
for row in matrix:
for item in row:
print(item, end=" ")
print()
Nested lists use chained indexing: matrix[row][col]. The outer index selects the row (inner list), and the inner index selects the element within that row.
Lists, Indexing, Negative Indexing, len(), Nested Lists