Skip to content

Lists

Creating and indexing

A list is written with square brackets. Lists can be empty or contain comma-separated item. Items can be any type and can even mixed:

scores = [88, 92, 79, 95]
names = ["Ada", "Grace", "Alan"]
mixed = ["Ada", 36, True]
empty = []

Lists can be indexed, concatenated and sliced exactly like strings. Indexes are 0-based:

scores[0]      # ⇒ 88     first
scores[-1]     # ⇒ 95     last
scores[1:3]    # ⇒ [92, 79]   a slice is a NEW list
len(scores)    # ⇒ 4

[2] + [3]      # ⇒ [2, 3]
[2] * 3        # ⇒ [2, 2, 2]

Mutating lists

Unlike strings, you can change a list's contents without making a new one:

scores = [88, 92, 79]
scores[0] = 100          # replace an element
print(scores)            # ⇒ [100, 92, 79]

Lists can contain other lists:

matrix = [[1,2,3], [4,5,6], [7,8,9]]

The list type defines several methods that change a list in place:

nums = [1, 2, 3]
nums.append(4)           # add ONE item to the end     → [1, 2, 3, 4]
nums.insert(0, 9)        # insert at an index           → [9, 1, 2, 3, 4]
nums.extend([5, 6])      # add several from another list→ [9, 1, 2, 3, 4, 5, 6]
nums.remove(9)           # remove first matching VALUE  → [1, 2, 3, 4, 5, 6]
last = nums.pop()        # remove & RETURN the last     → last=6
first = nums.pop(0)      # remove & return by index     → first=1
nums.sort()              # sort IN PLACE (ascending)
nums.sort(reverse=True)  # descending
nums.reverse()           # reverse in place
nums.clear()             # empty it

Mutating methods don't return a value

Generally, methods that change a list's contents in place return None.

A common mistake is to mutate and re-bind a reference to a list:

nums = nums.append(4)

This will not have the expected outcome. The expression will throw away your list and store None instead.

The exception is .pop() which removes a value from the list and returns it.

Non-mutating alternatives

It is often recommended to avoid changing a list in place, but what alternatives are there?

  • concatenation: .append() and .extend()
    nums = [1, 2, 3]    
    nums + [4]                  # → [1, 2, 3, 4]
    nums + [4,5,6]              # → [ 1, 2, 3, 4, 4, 6]
    
  • slicing: .insert()
    nums[:0] + [9] + nums[0:]   # → [9, 1, 2, 3]
    
  • non-mutating functions: non-mutating built-in functions for sorting and reversing lists:
    nums = [4,3,6,2]
    sorted(nums)                # → [2, 3, 4, 6]
    sorted(nums, reverse=True)  # → [6, 4, 3, 2]
    list(reversed(nums))        # → [2, 6, 3, 4]
    

Searching, couting, and aggregation

nums = [1, 2, 3]
3 in nums               # → True
nums.count(2)           # number of occurrences of 2 in the list
nums.index(3)           # position of first occurrence of 3 in the list
sum(nums)               # ⇒ total (works only for lists of numbers)
min(nums)               # → 1
max(nums)               # → 3

Lists from strings and back

text = 'the quick brown fox'
words = text.split()
print(len(words), "words")      # ⇒ 3 words

csv_header = ["Name", "Job Title", "Salary"]
';'.join(csv_header)

list gotchas

  • Aliasing

    a = [1, 2, 3]
    b = a               # → b is not a new list, but an alias of a
    
    b.append(4)
    print(b)            # ⇒ [1, 2, 3, 4]
    print(a)            # ⇒ [1, 2, 3, 4]
    
  • Copying

    nums = [1,2,3]
    nums.copy()     # → [1,2,3]
    

    .copy() only copies references of list items, not their values. For lists that contain other lists, this can lead to subtle bugs:

    nums = [[1], [1], [1]]
    copy = nums.copy()
    
    id(nums[0]) == id(copy[0])      # → True
    
    copy[0][0] = 5
    print(copy)                     # → [[5], [1], [1]]
    print(nums)                     # → [[5], [1], [1]]
    

    Watch out for this behavior too when you concatenate lists containing other lists:

    a = [[]]                 # a list containing an empty list
    b = [[1]] 
    c = a + b
    print(c)                 # → [[], [1]]
    
    c[0].append(3)
    print(c)                 # → [[3], [1]]
    print(a)                 # -> [[3]]
    
    x = y = [[]]
    x[0].append(1)
    print(x)                 # → [[1]]
    print(x)                 # → [[1]]
    
    x = [[1, 2, 3], [4, 5, 6]]
    y = x * 2
    y[1].clear()
    print(y)                 # → [[1, 2, 3], [], [1, 2, 3], []]
    print(x)                 # → [[1, 2, 3], []]
    

    This is why it is recommended to prefer creating a new list over changing a list in place