Skip to content

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]

nums = [0, 1, 2, 3, 4]
print([n * n for n in nums if n % 2 == 0])

Make a stay unchanged when we modify b

Expected: [1, 2, 3]

a = [1, 2, 3]
b = a              # bug: this aliases — b and a are the same list
b.append(99)
print(a)

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 returns None.

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) produces 0, 1, 2. Each value is doubled. The list ends at 4 because range(3) stops before 3.

Q3. Difference between append and extend?

  • No difference
  • append is for strings, extend is for numbers
  • append adds the argument as a single item; extend adds every item from the argument iterable
  • extend returns 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.

What's next

Tuples & Sets