PythonLists

Lists

A list is Python’s general-purpose container for an ordered collection of values. Unlike arrays in many other languages, a Python list can hold items of different types at once, grows and shrinks as you add or remove elements, and is one of the most frequently used data structures in everyday code — from storing a handful of numbers to building up rows of data read from a file.

Creating a List

You create a list with square brackets, separating items with commas. Lists can be empty, hold a single type, or mix types freely.

Python
fruits = ["apple", "banana", "cherry"]
numbers = [1, 2, 3, 4, 5]
mixed = ["Ada", 36, True, 3.14]
empty = []

print(fruits)  # ['apple', 'banana', 'cherry']
print(mixed)   # ['Ada', 36, True, 3.14]

# You can also build a list from any iterable with list()
letters = list("abc")
print(letters)  # ['a', 'b', 'c']
Note
Mixing types in a single list is legal, but it usually makes code harder to reason about. Most real-world lists hold items that are conceptually “the same kind of thing” — a list of usernames, a list of prices, a list of rows.
Lists Are Mutable

The defining feature of a list is that it can be changed after creation — you can add, remove, or replace items without creating a new list object. This is different from strings and tuples, which cannot be modified in place.

Python
scores = [10, 20, 30]
scores[1] = 99          # replace an item by position
print(scores)           # [10, 99, 30]

scores.append(40)       # add an item at the end
print(scores)           # [10, 99, 30, 40]

original = scores
original.append(50)
print(scores is original)  # True - same list object
print(scores)               # [10, 99, 30, 40, 50] - visible through both names
Tip
Because lists are mutable, assigning a list to a second name does not copy it. Both names point to the same underlying list, so a change made through one name is visible through the other. Use `list(original)` or `original.copy()` when you actually want an independent copy.
Indexing

Every item in a list has a numeric position, or index, starting at 0 for the first item. Python also supports negative indices, which count backward from the end of the list, with -1 referring to the last item.

Python
colors = ["red", "green", "blue", "yellow"]

print(colors[0])   # red    - first item
print(colors[2])   # blue   - third item
print(colors[-1])  # yellow - last item
print(colors[-2])  # blue   - second to last

# Indexing past the end raises an error
# print(colors[10])  # IndexError: list index out of range

Index

0

1

2

3

Value

"red"

"green"

"blue"

"yellow"

Negative index

-4

-3

-2

-1

Slicing Basics

Slicing lets you pull out a sub-list using the start:stop (and optional step) syntax. The start index is included, the stop index is excluded, and omitting either end defaults to “from the beginning” or “to the end.”

Python
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

print(numbers[2:5])    # [2, 3, 4]      - index 2 up to (not including) 5
print(numbers[:3])     # [0, 1, 2]      - from the start
print(numbers[7:])     # [7, 8, 9]      - to the end
print(numbers[::2])    # [0, 2, 4, 6, 8] - every second item
print(numbers[::-1])   # [9, 8, 7, ..., 0] - reversed copy

# Slicing never raises IndexError, even out of range
print(numbers[5:100])  # [5, 6, 7, 8, 9]
Note
A slice always returns a brand-new list, leaving the original untouched. This makes slicing a safe, non-destructive way to inspect or copy part of a list.
Nested Lists

Because a list can hold any object, it can hold other lists too. This is how you represent grids, matrices, or any table-like data in plain Python.

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

print(grid[0])      # [1, 2, 3]        - first row
print(grid[1][2])   # 6                - row 1, column 2
print(grid[-1][0])  # 7                - last row, first column

for row in grid:
    print(row)
# [1, 2, 3]
# [4, 5, 6]
# [7, 8, 9]
Warning
Be careful when creating a nested list by repeating a single inner list with `*`, such as `[[0] * 3] * 3`. All three rows end up referring to the exact same inner list object, so modifying one row silently changes every row.

Python
bad_grid = [[0] * 3] * 3
bad_grid[0][0] = 1
print(bad_grid)  # [[1, 0, 0], [1, 0, 0], [1, 0, 0]] - all rows changed!

# Correct way: build each row independently
good_grid = [[0] * 3 for _ in range(3)]
good_grid[0][0] = 1
print(good_grid)  # [[1, 0, 0], [0, 0, 0], [0, 0, 0]] - only row 0 changed
Common Operations

Beyond indexing and slicing, lists support a handful of operators and methods that come up constantly. The methods below are covered in full detail (with every parameter) on the List Methods page — this is the quick-start version.

Python
items = ["a", "b", "c"]

items.append("d")        # add one item at the end
print(items)              # ['a', 'b', 'c', 'd']

items.insert(1, "x")     # insert "x" at index 1
print(items)              # ['a', 'x', 'b', 'c', 'd']

items.remove("x")        # remove the first "x" found
print(items)              # ['a', 'b', 'c', 'd']

last = items.pop()       # remove and return the last item
print(last, items)        # d ['a', 'b', 'c']

items.extend(["e", "f"]) # append every item from another iterable
print(items)              # ['a', 'b', 'c', 'e', 'f']

combined = ["a", "b"] + ["c", "d"]  # concatenation
print(combined)                      # ['a', 'b', 'c', 'd']

repeated = ["x", "y"] * 3            # repetition
print(repeated)                       # ['x', 'y', 'x', 'y', 'x', 'y']

Operation

Effect

append(x)

Adds a single item x to the end of the list.

insert(i, x)

Inserts x at index i, shifting later items right.

remove(x)

Removes the first item equal to x; raises ValueError if absent.

pop([i])

Removes and returns the item at index i (default: last item).

extend(iterable)

Appends every item from another iterable, in place.

list_a + list_b

Returns a new list that is the concatenation of both.

list_a * n

Returns a new list with list_a’s contents repeated n times.

Checking Membership with `in`

The in operator checks whether a value exists anywhere in a list, returning a plain bool. Its counterpart, not in, checks for absence.

Python
fruits = ["apple", "banana", "cherry"]

print("banana" in fruits)      # True
print("mango" in fruits)       # False
print("mango" not in fruits)   # True

if "apple" in fruits:
    print("We have apples!")   # We have apples!
Note
`in` on a list scans items one by one until it finds a match, so checking membership is an O(n) operation. If you need very fast repeated membership checks on a large, unordered collection, a `set` is usually a better fit than a list.
Lists vs Arrays

If you have used a language like Java, C, or C#, you might expect a Python list to behave like an array. Conceptually they are quite different.

  • A Python list can hold values of mixed types; a traditional array is typically fixed to one type.

  • A Python list resizes automatically as you append or remove items; many arrays have a fixed size set at creation.

  • Python does have a fixed-type, memory-compact array module and third-party arrays like NumPy’s ndarray, which behave much more like classic arrays and are faster for large numeric workloads.

  • For everyday general-purpose collections, the built-in list is almost always the right default.

Length and Iteration

Two things you will do with nearly every list: ask how many items it has with len(), and loop over its items directly with for.

Python
fruits = ["apple", "banana", "cherry"]

print(len(fruits))  # 3

for fruit in fruits:
    print(fruit)
# apple
# banana
# cherry

for index, fruit in enumerate(fruits):
    print(index, fruit)
# 0 apple
# 1 banana
# 2 cherry
Tip
Prefer `for item in my_list:` over manually indexing with a counter whenever you don’t need the index. When you do need both the index and the value, `enumerate()` is cleaner and less error-prone than tracking a counter by hand.