Skip to content

Python Lists & Methods

A list (list) Mutable is an ordered collection of items. Lists can hold any mix of types, can grow and shrink, and are the most commonly used data structure in Python.

TaskSyntaxExample
Create[...], list(iterable)list('Dev') gives ['D', 'e', 'v']
Lengthlen(lst)len([1, 2, 3]) gives 3
Accesslst[i], lst[-1]['a', 'b'][-1] gives 'b'
Slicelst[start:stop:step][1, 2, 3, 4][1:3] gives [2, 3]
Add one.append(x), .insert(i, x)lst.append(4)
Add many.extend(iterable), +lst.extend([4, 5])
Remove.remove(x), .pop(i), del lst[i]lst.pop()
Searchx in lst, .index(x), .count(x)2 in [1, 2, 3] gives True
Sort.sort(), sorted(lst)sorted([3, 1, 2]) gives [1, 2, 3]
Build[expr for x in iterable][n * 2 for n in range(3)] gives [0, 2, 4]

creating_lists.py
empty = []
cities = ['London', 'Tokyo']
mixed = [1, 'two', 3.0, [4, 5]] # Any types, even other lists
chars = list('Dev') # ['D', 'e', 'v']
digits = list(range(5)) # [0, 1, 2, 3, 4]
basic_operations.py
nums = [10, 20, 30]
print(len(nums)) # 3
print(nums[0]) # 10
print(nums[-1]) # 30, negative indices count from the end
nums[1] = 25 # Lists are mutable, so items can be replaced
print(nums) # [10, 25, 30]
print(30 in nums) # True
print([1, 2] + [3]) # [1, 2, 3], concatenation creates a new list
print([0] * 3) # [0, 0, 0], repetition
print(min(nums), max(nums), sum(nums)) # 10 30 65

Extract part of a list with lst[start:stop:step]. As with strings, stop is excluded, omitted bounds default to the start or end, and a negative step walks backward. Slicing always returns a new list.

ExpressionResult for items = ['a', 'b', 'c', 'd', 'e']
items[1:4]['b', 'c', 'd']
items[:2]['a', 'b']
items[3:]['d', 'e']
items[-2:]['d', 'e']
items[::2]['a', 'c', 'e']
items[::-1]['e', 'd', 'c', 'b', 'a']
items[:]['a', 'b', 'c', 'd', 'e'] (a shallow copy)
items[10:][] (out-of-range slices never raise errors)

Slices can also be assigned to or deleted:

slice_assignment.py
items = ['a', 'b', 'c', 'd', 'e']
items[1:3] = ['X'] # Replaces two items with one
print(items) # ['a', 'X', 'd', 'e']
del items[1:3] # Deletes a range
print(items) # ['a', 'e']

MethodBehaviorComplexity
.append(x)Adds x as a single item at the endO(1)
.extend(iterable)Adds each item of the iterable at the endO(k)
.insert(i, x)Inserts x at index i, shifting later items rightO(n)
adding_items.py
numbers = [1, 2, 3]
extras = [4, 5]
numbers.append(extras) # [1, 2, 3, [4, 5]], nests the list as one item
numbers = [1, 2, 3]
numbers.extend(extras) # [1, 2, 3, 4, 5], adds items individually
numbers.insert(2, 2.5) # [1, 2, 2.5, 3, 4, 5]
numbers += [6, 7] # [1, 2, 2.5, 3, 4, 5, 6, 7], same as extend()

Method / KeywordBehavior
.pop()Removes and returns the last item
.pop(i)Removes and returns the item at index i
.remove(x)Removes the first item equal to x
del lst[i]Deletes the item at i (or a slice) without returning it
.clear()Removes every item, leaving []
removing_items.py
numbers = [10, 20, 30, 40, 50, 50]
numbers.remove(50) # [10, 20, 30, 40, 50], only the first match is removed
removed = numbers.pop(1) # removed is 20, list is [10, 30, 40, 50]
last = numbers.pop() # last is 50, list is [10, 30, 40]
del numbers[0] # [30, 40]
numbers.clear() # []

To remove every match, rebuild the list instead:

remove_all_matches.py
numbers = [10, 50, 20, 50]
numbers = [n for n in numbers if n != 50] # [10, 20]

searching.py
langs = ['Rust', 'Java', 'Python', 'Java']
print('Java' in langs) # True
print(langs.index('Java')) # 1, first occurrence
print(langs.index('Java', 2)) # 3, start searching at index 2
print(langs.count('Java')) # 2
print(langs.index('Go')) # ValueError: 'Go' is not in list

ToolBehaviorReturns
lst.sort()Sorts the list in placeNone
sorted(lst)Builds a new sorted list, original untouchedA new list
lst.reverse()Reverses the list in placeNone
reversed(lst)Iterates in reverse without changing the listAn iterator
nums = [19, 2, 35, 1]
nums.sort() # nums is now [1, 2, 19, 35]
nums.sort(reverse=True) # nums is now [35, 19, 2, 1]

Use when you no longer need the original order.

The key function is applied to each item, and the results decide the order.

sort_with_key.py
words = ['banana', 'Cherry', 'apple']
print(sorted(words)) # ['Cherry', 'apple', 'banana'], uppercase sorts first
print(sorted(words, key=str.lower)) # ['apple', 'banana', 'Cherry']
print(sorted(words, key=len)) # ['apple', 'Cherry', 'banana']
people = [('Ann', 31), ('Bob', 25)]
print(sorted(people, key=lambda p: p[1])) # [('Bob', 25), ('Ann', 31)]

Assigning a list to another name does not copy it. Both names refer to the same list.

aliasing.py
a = [1, 2, 3]
b = a # Alias: one list, two names
b.append(4)
print(a) # [1, 2, 3, 4], a changed too
c = a.copy() # A genuine copy (also: a[:] or list(a))
c.append(5)
print(a) # [1, 2, 3, 4], unaffected

.copy() is shallow: it creates a new outer list, but any nested lists are still shared. Use copy.deepcopy() for full independence.

shallow_vs_deep.py
import copy
original = [1, 2, [3, 4]]
shallow = original.copy()
shallow[2].append(5)
print(original) # [1, 2, [3, 4, 5]], the inner list is shared
deep = copy.deepcopy(original)
deep[2].append(6)
print(original) # [1, 2, [3, 4, 5]], unchanged

Unpacking assigns list items to several variables in one statement.

unpacking.py
point = [10, 20]
x, y = point # x = 10, y = 20
first, *rest = [1, 2, 3, 4] # first = 1, rest = [2, 3, 4]
*head, last = [1, 2, 3, 4] # head = [1, 2, 3], last = 4
first, *middle, last = [1, 2, 3, 4, 5] # 1, [2, 3, 4], 5
a, b = 1, 2
a, b = b, a # Swap without a temp variable
x, y = [1, 2, 3] # ValueError: too many values to unpack (expected 2)

The starred name (*rest) collects “everything else” into a list and may appear once.


looping.py
langs = ['Rust', 'Java', 'Python']
for lang in langs:
print(lang)
for index, lang in enumerate(langs, start=1):
print(index, lang) # 1 Rust, 2 Java, 3 Python
names = ['Ann', 'Bob']
scores = [90, 85]
for name, score in zip(names, scores):
print(name, score) # Ann 90, Bob 85

A list can contain other lists. Chain indices to reach inner items: lst[outer][inner].

nested_lists.py
dev = ['Alice', 25, ['Python', 'Rust', 'C++']]
print(dev[2][1]) # 'Rust'
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
]
print(matrix[1][2]) # 6, row 1, column 2
for row in matrix:
for value in row:
print(value, end=' ') # 1 2 3 4 5 6 7 8 9

Repetition copies references, so [[0] * 3] * 3 creates three names for one row. Use a comprehension to build independent rows.

grid_pitfall.py
bad = [[0] * 3] * 3
bad[0][0] = 1
print(bad) # [[1, 0, 0], [1, 0, 0], [1, 0, 0]], all rows changed
good = [[0] * 3 for _ in range(3)]
good[0][0] = 1
print(good) # [[1, 0, 0], [0, 0, 0], [0, 0, 0]]

A comprehension builds a new list from an iterable in a single expression.

comprehension_basics.py
# Traditional loop
even_numbers = []
for num in range(21):
if num % 2 == 0:
even_numbers.append(num)
# Comprehension, same result
even_numbers = [num for num in range(21) if num % 2 == 0]
print(even_numbers) # [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
GoalPatternExample
Transform each item[expr for x in items][n ** 2 for n in range(1, 6)] gives [1, 4, 9, 16, 25]
Keep only some items[expr for x in items if cond][n for n in range(10) if n > 6] gives [7, 8, 9]
Choose between two values[a if cond else b for x in items]['Even' if n % 2 == 0 else 'Odd' for n in [1, 2]] gives ['Odd', 'Even']
Flatten nested lists[x for row in rows for x in row][x for row in [[1, 2], [3]] for x in row] gives [1, 2, 3]
comprehension_conditional.py
numbers = [1, 2, 3, 4, 5]
result = [(num, 'Even') if num % 2 == 0 else (num, 'Odd') for num in numbers]
print(result)
# [(1, 'Odd'), (2, 'Even'), (3, 'Odd'), (4, 'Even'), (5, 'Odd')]

Big-O notation describes how an operation’s cost grows as the list size n grows.

  • O(1) (constant): the cost stays the same for 5 items or 5,000,000.
  • O(n) (linear): the cost grows with the list, because Python may inspect every item.
  • O(k): the cost depends on the number of items k being added or extracted.
OperationComplexitySpeed
lst[i], len(lst)O(1)Fast
.append(x), .pop() (last item)O(1)Fast
.extend(iterable), slicingO(k)Proportional
.insert(i, x), .pop(i), .remove(x), del lst[i]O(n)Linear
x in lst, .index(x), .count(x)O(n)Linear
.copy(), .reverse(), min(), max(), sum()O(n)Linear
.sort(), sorted()O(n log n)Timsort

  1. Clean. Filter out unwanted items and normalize the rest with a comprehension.

  2. Deduplicate. dict.fromkeys() removes duplicates while preserving order. A set would lose the order.

  3. Sort. Use .sort() or sorted(), with a key when needed.

  4. Present. Use ', '.join(items) to turn the list into text.

clean_list_pipeline.py
raw = [' Banana', 'apple ', 'Cherry', 'apple', '']
cleaned = [item.strip().lower() for item in raw if item.strip()]
print(cleaned) # ['banana', 'apple', 'cherry', 'apple']
unique = list(dict.fromkeys(cleaned))
print(unique) # ['banana', 'apple', 'cherry']
unique.sort()
print(', '.join(unique)) # apple, banana, cherry

MistakeProblemFix
nums = nums.sort().sort() returns Nonenums.sort() or nums = sorted(nums)
lst = lst.append(x).append() returns Nonelst.append(x)
b = a expecting a copyBoth names share one listb = a.copy()
[[0] * 3] * 3Rows are the same object[[0] * 3 for _ in range(3)]
Removing items inside a for loopItems get skippedUse a comprehension or loop over lst[:]
.remove(x) / .index(x) on a missing valueValueErrorCheck x in lst first
.append([4, 5]) expecting two itemsAdds one nested list.extend([4, 5])
lst[len(lst)]IndexError, the last index is len(lst) - 1lst[-1]
def f(items=[])The default list is shared across callsdef f(items=None) then items = items or []
lst = lst + [x] in a loopBuilds a new list every timelst.append(x)
Shallow copy of nested listsInner lists are still sharedcopy.deepcopy(lst)
  1. Use .append() for one item and .extend() for many.
  2. Use sorted() when you must keep the original order, and .sort() when you do not.
  3. Copy deliberately: .copy() for flat lists, copy.deepcopy() for nested ones.
  4. Never modify a list while iterating over it. Build a new one instead.
  5. Use enumerate() and zip() instead of manual index counters.
  6. Use comprehensions for simple transforms and filters, and plain loops for anything complex.
  7. Prefer unpacking (first, *rest = items) over indexing for readability.
  8. Reach for deque or set when front-of-list operations or fast membership checks dominate.