Lists¶
A list is an ordered, changeable collection of items. The most-used Python data structure.
Creating a list¶
empty = []
numbers = [1, 2, 3, 4, 5]
mixed = ["apple", 42, True, 3.14]
nested = [[1, 2], [3, 4], [5, 6]]
print(empty, numbers, mixed, nested)
Accessing items¶
Same indexing as strings: 0-based, negatives count from the end, supports slicing.
fruits = ["apple", "banana", "cherry", "date", "elderberry"]
print(fruits[0]) # 'apple'
print(fruits[-1]) # 'elderberry'
print(fruits[1:4]) # ['banana', 'cherry', 'date']
print(fruits[::-1]) # reversed
print(len(fruits)) # 5
Lists are mutable — change in place¶
fruits = ["apple", "banana", "cherry"]
fruits[1] = "blueberry"
print(fruits) # ['apple', 'blueberry', 'cherry']
Adding items¶
fruits = ["apple", "banana"]
fruits.append("cherry") # add to end
print(fruits) # ['apple', 'banana', 'cherry']
fruits.insert(1, "blueberry") # insert at index 1
print(fruits) # ['apple', 'blueberry', 'banana', 'cherry']
fruits.extend(["date", "fig"]) # add many items
print(fruits) # ['apple', 'blueberry', 'banana', 'cherry', 'date', 'fig']
# Same with + (creates new list)
combined = fruits + ["grape"]
print(combined)
Removing items¶
fruits = ["apple", "banana", "cherry", "banana"]
fruits.remove("banana") # remove FIRST 'banana'
print(fruits) # ['apple', 'cherry', 'banana']
last = fruits.pop() # remove + return LAST item
print(last, fruits) # 'banana' ['apple', 'cherry']
second = fruits.pop(1) # remove + return at index 1
print(second, fruits) # 'cherry' ['apple']
del fruits[0] # remove by index, no return
print(fruits) # []
fruits = [1, 2, 3]
fruits.clear() # remove everything
print(fruits) # []
Searching¶
fruits = ["apple", "banana", "cherry", "banana"]
print("apple" in fruits) # True
print("mango" in fruits) # False
print(fruits.index("banana")) # 1 — index of FIRST match
print(fruits.count("banana")) # 2 — how many times
Sorting & reversing¶
nums = [3, 1, 4, 1, 5, 9, 2, 6]
nums.sort() # sort IN-PLACE
print(nums) # [1, 1, 2, 3, 4, 5, 6, 9]
nums.sort(reverse=True)
print(nums) # [9, 6, 5, 4, 3, 2, 1, 1]
nums.reverse()
print(nums) # [1, 1, 2, 3, 4, 5, 6, 9]
# sorted() returns a NEW sorted list — doesn't modify the original
nums = [3, 1, 4]
result = sorted(nums)
print(result, nums) # [1, 3, 4] [3, 1, 4]
Sort by a custom key:
words = ["banana", "pie", "Washington", "book"]
# Sort by length
print(sorted(words, key=len)) # ['pie', 'book', 'banana', 'Washington']
# Sort case-insensitive
print(sorted(words, key=str.lower))
Iteration patterns¶
fruits = ["apple", "banana", "cherry"]
# basic
for fruit in fruits:
print(fruit)
# with index
for i, fruit in enumerate(fruits):
print(f"{i}: {fruit}")
List comprehensions — Pythonic loops¶
A shorter way to build a list:
# Old way
squares = []
for x in range(10):
squares.append(x * x)
print(squares)
# Pythonic — same result in one line
squares = [x * x for x in range(10)]
print(squares)
# With a filter
evens = [x for x in range(20) if x % 2 == 0]
print(evens)
# Transform + filter
upper_long_words = [w.upper() for w in ["hi", "hello", "world", "py"] if len(w) > 3]
print(upper_long_words)
Common list functions¶
nums = [3, 1, 4, 1, 5, 9, 2, 6]
print(len(nums)) # 8
print(min(nums)) # 1
print(max(nums)) # 9
print(sum(nums)) # 31
print(sum(nums) / len(nums)) # 3.875 — average
# any / all
print(any(x > 5 for x in nums)) # True — at least one
print(all(x > 0 for x in nums)) # True — every one
Copying lists (watch out!)¶
b = a makes b and a the same list — changes to one affect the other.
a = [1, 2, 3]
b = a # NOT a copy
b.append(4)
print(a) # [1, 2, 3, 4] ← a also changed!
# Proper copies:
a = [1, 2, 3]
b = a.copy() # method
c = a[:] # slice
d = list(a) # constructor
b.append(99)
print(a, b) # [1, 2, 3] [1, 2, 3, 99]
For nested lists, even .copy() is shallow. Use copy.deepcopy():
import copy
a = [[1, 2], [3, 4]]
b = a.copy()
b[0].append("hi")
print(a) # [[1, 2, 'hi'], [3, 4]] ← inner list still shared
a = [[1, 2], [3, 4]]
b = copy.deepcopy(a)
b[0].append("hi")
print(a, b) # a unchanged; b has the 'hi'
2D lists (matrix)¶
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
]
print(matrix[0]) # [1, 2, 3]
print(matrix[1][2]) # 6
# Iterate
for row in matrix:
for value in row:
print(value, end=" ")
print()
# Build with a list comprehension
grid = [[i*j for j in range(1, 4)] for i in range(1, 4)]
print(grid) # [[1, 2, 3], [2, 4, 6], [3, 6, 9]]
Mini-project — Find the second largest¶
nums = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3]
unique_sorted = sorted(set(nums), reverse=True)
print(f"Second largest: {unique_sorted[1]}")
Practice¶
What does this print?
Expected: [0, 4, 16]
Make a stay unchanged when we modify b
Expected: [1, 2, 3]
Quiz — Quick check¶
What you remember
Q1. Which method modifies the list in place instead of returning a new one?
-
sorted(nums) -
nums.sort() -
list(nums) -
nums[:]
Why:
sorted()is a built-in that returns a new sorted list..sort()is a list method that mutates the original and returnsNone.
Q2. What does [x*2 for x in range(3)] produce?
-
[0, 1, 2] -
[0, 2, 4] -
[2, 4, 6] -
[0, 2, 4, 6]
Why:
range(3)produces0, 1, 2. Each value is doubled. The list ends at 4 becauserange(3)stops before 3.
Q3. Difference between append and extend?
- No difference
-
appendis for strings,extendis for numbers -
appendadds the argument as a single item;extendadds every item from the argument iterable -
extendreturns a new list
Why:
nums.append([1,2])produces[..., [1, 2]](nested).nums.extend([1,2])produces[..., 1, 2](flat).
Common doubts¶
Why does modifying b also modify a when I wrote b = a?
Because b = a makes both names point to the same list object in memory — it's not a copy. To make an actual copy, use b = a.copy(), b = list(a), or b = a[:]. For nested lists, even those are shallow copies — use copy.deepcopy(a) if you need to copy nested structures too.
List comprehension vs for loop — which is better?
Comprehensions are clearer and faster for simple transformations and filters. Use a for loop when the body is multi-line, has side effects (like print), or is too complex to fit comfortably on one line.
Are Python lists like arrays in C/Java?
Lists are dynamic arrays under the hood — they grow automatically — but they can hold any types mixed together, unlike C arrays. For heavy numeric work, use numpy arrays — they're typed and ~10–100× faster than Python lists for math.