Creating Lists
A list is created with square brackets []. Elements are separated by commas. Lists can contain any data types β even a mix of them.
# Empty list
empty = []
# List of strings
fruits = ["apple", "banana", "cherry"]
# List of integers
primes = [2, 3, 5, 7, 11, 13]
# Mixed types
mixed = [42, "hello", 3.14, True, None]
# List from range
numbers = list(range(1, 6)) # [1, 2, 3, 4, 5]
print(fruits)
print(primes)
print(mixed)
print(numbers)
print(f"Length of fruits: {len(fruits)}")
Elements stay in the order you add them (ordered), and you can change any element after creation (mutable). This distinguishes lists from tuples (immutable) and sets (unordered).
Indexing β Accessing Elements
Each element has an index starting at 0. Python also supports negative indexing β counting from the end, where -1 is the last element.
colors = ["red", "green", "blue", "yellow", "purple"]
# 0 1 2 3 4 (positive)
# -5 -4 -3 -2 -1 (negative)
# Positive indexing
print(colors[0]) # red (first)
print(colors[2]) # blue (third)
print(colors[4]) # purple (last)
# Negative indexing
print(colors[-1]) # purple (last)
print(colors[-2]) # yellow (second to last)
print(colors[-5]) # red (first)
# Modifying an element
colors[1] = "lime"
print(colors) # ['red', 'lime', 'blue', 'yellow', 'purple']
Slicing β Accessing Sub-Lists
Slicing extracts a portion of a list. The syntax is list[start:stop:step]. The stop index is exclusive (not included).
| Syntax | Meaning |
|---|---|
a[2:5] | Elements at index 2, 3, 4 |
a[:3] | First 3 elements (0, 1, 2) |
a[3:] | From index 3 to end |
a[:] | Shallow copy of entire list |
a[::2] | Every second element |
a[::-1] | Reversed list |
nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(nums[2:6]) # [2, 3, 4, 5]
print(nums[:4]) # [0, 1, 2, 3]
print(nums[7:]) # [7, 8, 9]
print(nums[::2]) # [0, 2, 4, 6, 8] β every 2nd
print(nums[1::2]) # [1, 3, 5, 7, 9] β odd indices
print(nums[::-1]) # [9, 8, 7, ..., 0] β reversed
print(nums[-3:]) # [7, 8, 9] β last 3
List Methods
Lists have many built-in methods. Here is a comprehensive reference:
| Method | Description | Returns |
|---|---|---|
append(x) | Add x to the end | None |
extend(iterable) | Add all items from iterable to end | None |
insert(i, x) | Insert x before index i | None |
remove(x) | Remove first occurrence of x | None |
pop(i=-1) | Remove and return item at index i | The removed item |
clear() | Remove all items | None |
index(x) | Index of first occurrence of x | int |
count(x) | Number of times x appears | int |
sort() | Sort in place (ascending by default) | None |
reverse() | Reverse in place | None |
copy() | Shallow copy of the list | new list |
animals = ["cat", "dog", "bird"]
# append - add to end
animals.append("fish")
print(animals) # ['cat', 'dog', 'bird', 'fish']
# extend - add multiple items
animals.extend(["hamster", "rabbit"])
print(animals) # ['cat', 'dog', 'bird', 'fish', 'hamster', 'rabbit']
# insert - add at specific position
animals.insert(1, "snake")
print(animals) # ['cat', 'snake', 'dog', 'bird', 'fish', 'hamster', 'rabbit']
# remove - remove by value
animals.remove("snake")
print(animals) # ['cat', 'dog', 'bird', 'fish', 'hamster', 'rabbit']
# pop - remove and return by index
last = animals.pop() # removes last
print(last) # rabbit
specific = animals.pop(1) # removes index 1
print(specific) # dog
print(animals) # ['cat', 'bird', 'fish', 'hamster']
Sorting and Reversing
scores = [88, 42, 95, 67, 71, 100, 55]
# sort() β modifies the list in place
scores.sort()
print("Ascending:", scores)
scores.sort(reverse=True)
print("Descending:", scores)
# sorted() β returns a NEW sorted list, original unchanged
original = [3, 1, 4, 1, 5, 9, 2, 6]
sorted_copy = sorted(original)
print("Original:", original)
print("Sorted copy:", sorted_copy)
# reverse() β reverses in place
words = ["banana", "apple", "cherry"]
words.reverse()
print("Reversed:", words)
# Sort strings by length using key
names = ["Charlie", "Alice", "Bob", "David"]
names.sort(key=len)
print("Sorted by length:", names)
list.sort() modifies the list in place and returns None. sorted(list) returns a new list and leaves the original unchanged. A common bug is writing my_list = my_list.sort() β this sets my_list to None!
List Operations
a = [1, 2, 3]
b = [4, 5, 6]
# Concatenation
combined = a + b
print(combined) # [1, 2, 3, 4, 5, 6]
# Repetition
repeated = a * 3
print(repeated) # [1, 2, 3, 1, 2, 3, 1, 2, 3]
# Membership
print(2 in a) # True
print(7 in a) # False
print(7 not in a) # True
# Length, min, max, sum
numbers = [4, 2, 8, 1, 9, 3]
print(len(numbers)) # 6
print(min(numbers)) # 1
print(max(numbers)) # 9
print(sum(numbers)) # 27
# Checking if list is empty
my_list = []
if not my_list:
print("The list is empty!")
Nested Lists (2D Lists)
A list can contain other lists, forming a 2D grid (matrix). Access elements with chained indexing: matrix[row][col].
# 3x3 matrix (list of lists)
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
# Access specific element: row 1, col 2
print(matrix[1][2]) # 6
# Print the full matrix nicely
for row in matrix:
print(row)
# Get a whole row
print("Row 0:", matrix[0]) # [1, 2, 3]
# Get a whole column using list comprehension
col_1 = [row[1] for row in matrix]
print("Column 1:", col_1) # [2, 5, 8]
# Modify a nested element
matrix[0][0] = 99
print("Modified matrix[0][0]:", matrix[0])
Copying Lists Correctly
original = [1, 2, 3, 4, 5]
# WRONG: assignment creates a reference, not a copy!
wrong_copy = original
wrong_copy.append(99)
print("Original (modified!):", original) # [1, 2, 3, 4, 5, 99]
# Correct shallow copy methods
original = [1, 2, 3, 4, 5]
copy1 = original.copy()
copy2 = original[:]
copy3 = list(original)
copy1.append(99)
print("Original (unchanged):", original) # [1, 2, 3, 4, 5]
print("copy1:", copy1) # [1, 2, 3, 4, 5, 99]
List Comprehensions β Preview
List comprehensions provide a concise syntax to build a new list from an existing iterable. They are covered in full in the List Comprehensions lesson, but here is a taste:
# Classic loop to build a list
squares = []
for n in range(1, 6):
squares.append(n ** 2)
# Same thing as a list comprehension
squares = [n ** 2 for n in range(1, 6)]
print(squares) # [1, 4, 9, 16, 25]
# With a filter: only even squares
even_squares = [n ** 2 for n in range(1, 11) if n % 2 == 0]
print(even_squares) # [4, 16, 36, 64, 100]
# Transform existing list: uppercase all strings
words = ["hello", "world", "python"]
upper = [w.upper() for w in words]
print(upper) # ['HELLO', 'WORLD', 'PYTHON']
ποΈ Practical Exercise
- Create a list of 5 favourite movies. Append a 6th, then insert one at position 2. Print the result.
- Given
numbers = [5, 2, 8, 1, 9, 3, 7, 4, 6], sort it ascending, then print the top 3 highest scores using slicing. - Write code that removes all occurrences of the value
0from a list using a loop andremove(). - Create a 3Γ3 identity matrix (1s on the diagonal, 0s elsewhere) as a nested list and print it row by row.
- Use a list comprehension to create a list of all words longer than 4 characters from a given sentence.
π₯ Challenge
Build a simple to-do list manager using a list. Implement functions: add_task(tasks, task), complete_task(tasks, task) (removes from active, adds to completed list), show_tasks(tasks, completed) which prints active and completed tasks with numbering. Demonstrate adding 5 tasks, completing 2, and showing the final state. Bonus: sort the active tasks alphabetically before displaying.
Interview Questions on Python Lists
- What is the difference between a list and a tuple in Python?
- What does negative indexing mean? What does
my_list[-1]return? - What is the difference between
append()andextend()? - What is the difference between
remove()andpop()? - What is the difference between
sort()andsorted()? Which modifies the original? - How do you make a copy of a list? Why does
b = anot create a copy? - How do you access elements in a nested (2D) list?
- What is a list comprehension? Write one that produces the squares of even numbers from 1 to 20.
π Summary
- Lists are ordered, mutable sequences created with square brackets
[]. - Access elements by index (0-based); negative indices count from the end.
- Slicing
[start:stop:step]extracts sub-lists; the stop index is exclusive. append()adds one item to the end;extend()adds all items from another iterable.insert(i, x)adds at a specific position;remove(x)deletes by value;pop(i)deletes by index and returns the removed item.sort()sorts in place (returns None);sorted()returns a new sorted list.- Use
list.copy()orlist[:]for shallow copies β neverb = aif you need independence. - Nested lists form 2D structures accessed with
matrix[row][col]. - List comprehensions provide a concise one-line syntax for transforming or filtering lists.
Related Topics
Frequently Asked Questions
Yes. Python lists can hold any mix of types: integers, floats, strings, booleans, other lists, objects β even None. For example: [1, "hello", 3.14, True, [2, 3]] is a valid list. In practice, it's best to keep lists homogeneous (same type) for clarity and to enable sorting.
append() is O(1) amortized. insert(i, x) and remove(x) are O(n) because elements must be shifted. Index access list[i] is O(1). in (membership test) is O(n). sort() is O(n log n). For fast membership tests, use a set instead.
A shallow copy creates a new list but the elements inside are still references to the same objects. For nested lists, modifying a nested list in the copy will affect the original. A deep copy (from import copy; copy.deepcopy()) recursively copies every nested object, making them fully independent.
The quickest way is list(set(my_list)), but this loses the original order. To preserve order while removing duplicates, use list(dict.fromkeys(my_list)) (Python 3.7+) or iterate with a seen set: seen = set(); result = [x for x in my_list if not (x in seen or seen.add(x))].
Yes. list[::-1] is the idiomatic way to reverse a list via slicing. With a negative step, the start and stop logic also flips: list[8:2:-1] gives elements from index 8 down to index 3 (stop is exclusive). This can be confusing, so for explicit reversal, reversed() or list.reverse() are often clearer.