Python Lists — The Complete Guide
💡 Lists are the most widely used data structure in Python. This comprehensive guide covers everything from basic creation to advanced techniques, giving you complete mastery over Python's most versatile collection type.
Learning Objectives
By the end of this tutorial, you will be able to:
- Create lists using various methods and understand when to use each
- Use indexing and slicing to access and manipulate list elements
- Apply all built-in list methods confidently
- Use all related built-in functions:
len(),min(),max(),sum(),sorted(),reversed(),enumerate(),zip(),map(),filter(),any(),all() - Write basic and intermediate list comprehensions
- Understand nested lists and 2D data structures
- Recognize performance implications of different operations
- Avoid common pitfalls that trip up Python developers
What Are Lists?
A list is an ordered, mutable collection in Python. Think of it as a container that holds multiple items in a specific sequence.
python
mixed = [42, "hello", 3.14, True, None, [1, 2, 3]]
print(mixed)
# Output: [42, 'hello', 3.14, True, None, [1, 2, 3]]
Key Characteristics
| Characteristic | Description | Example |
|---|---|---|
| Ordered | Elements maintain their insertion order | [1, 2, 3] is different from [3, 2, 1] |
| Mutable | Elements can be changed after creation | lst[0] = 99 works fine |
| Dynamic | Can grow or shrink at runtime | lst.append(4) adds an element |
| Heterogeneous | Can mix different types | [1, "two", 3.0] is valid |
| Duplicates allowed | Same value can appear multiple times | [1, 1, 2, 3] is valid |
| Zero-indexed | First element is at index 0 | lst[0] gets the first item |
Creating Lists
1. Literal Syntax (Most Common)
python
empty = []
fruits = ["apple", "banana", "cherry"]
numbers = [1, 2, 3, 4, 5]
mixed = [1, "two", 3.0, True]
2. The list() Constructor
python
chars = list("Python") # ['P', 'y', 't', 'h', 'o', 'n']
numbers = list(range(1, 6)) # [1, 2, 3, 4, 5]
from_tuple = list((10, 20, 30)) # [10, 20, 30]
3. List Multiplication
python
zeros = [0] * 5 # [0, 0, 0, 0, 0]
pattern = [0, 1] * 4 # [0, 1, 0, 1, 0, 1, 0, 1]
# WARNING: Be careful with mutable objects
nested = [[0]] * 3
nested[0][0] = 99
print(nested) # [[99], [99], [99]] — All sublists are the same object!
4. List Comprehension
python
squares = [x**2 for x in range(5)]
print(squares) # [0, 1, 4, 9, 16]
evens = [x for x in range(10) if x % 2 == 0]
print(evens) # [0, 2, 4, 6, 8]
Indexing and Slicing
python
fruits = ["apple", "banana", "cherry", "date", "elderberry"]
# 0 1 2 3 4
print(fruits[0]) # apple
print(fruits[2]) # cherry
print(fruits[-1]) # elderberry
# Slicing
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(numbers[2:5]) # [2, 3, 4]
print(numbers[:4]) # [0, 1, 2, 3]
print(numbers[6:]) # [6, 7, 8, 9]
print(numbers[::2]) # [0, 2, 4, 6, 8]
print(numbers[::-1]) # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
Slice Assignment (Mutating Slices)
python
letters = ['a', 'b', 'c', 'd', 'e']
letters[1:3] = ['B', 'C', 'D'] # Replace 2 elements with 3
print(letters) # ['a', 'B', 'C', 'D', 'd', 'e']
letters[1:1] = ['X', 'Y'] # Insert before index 1
print(letters) # ['a', 'X', 'Y', 'B', 'C', 'D', 'd', 'e']
letters[2:5] = [] # Remove elements at indices 2, 3, 4
print(letters) # ['a', 'X', 'B', 'd', 'e']
All List Methods
Adding Elements
| Method | Description | Time Complexity |
|---|---|---|
append(x) | Add x to the end | O(1) amortized |
extend(iterable) | Add all items from iterable | O(k) |
insert(i, x) | Insert x at position i | O(n) |
python
# append() — adds a single element to the end
fruits = ["apple", "banana"]
fruits.append("cherry")
print(fruits) # ['apple', 'banana', 'cherry']
# append a list as a single element
fruits.append(["date", "elderberry"])
print(fruits) # ['apple', 'banana', 'cherry', ['date', 'elderberry']]
# extend() — adds multiple elements from an iterable
fruits = ["apple", "banana"]
fruits.extend(["cherry", "date"])
print(fruits) # ['apple', 'banana', 'cherry', 'date']
# extend vs append
a = [1, 2]
a.extend([3, 4]) # Adds 3 and 4 as separate elements
print(a) # [1, 2, 3, 4]
b = [1, 2]
b.append([3, 4]) # Adds [3, 4] as a single element
print(b) # [1, 2, [3, 4]]
# insert() — adds element at a specific position
names = ["Alice", "Bob", "David"]
names.insert(2, "Charlie") # Insert "Charlie" at index 2
print(names) # ['Alice', 'Bob', 'Charlie', 'David']
names.insert(0, "Zara") # Insert at the beginning
print(names) # ['Zara', 'Alice', 'Bob', 'Charlie', 'David']
Removing Elements
| Method | Description | Time Complexity |
|---|---|---|
remove(x) | Remove first occurrence of x | O(n) |
pop(i) | Remove and return element at index i | O(n) for i≠-1, O(1) for i=-1 |
del lst[i] | Remove element at index i | O(n) |
clear() | Remove all elements | O(n) |
python
# remove() — removes first occurrence of a value
colors = ["red", "blue", "green", "blue"]
colors.remove("blue")
print(colors) # ['red', 'green', 'blue']
# pop() — removes and returns element at index
stack = [10, 20, 30, 40, 50]
last = stack.pop()
print(last) # 50
print(stack) # [10, 20, 30, 40]
second = stack.pop(1)
print(second) # 20
# del — keyword for removing elements
data = [10, 20, 30, 40, 50]
del data[2] # Remove element at index 2
del data[1:3] # Remove a slice
del data[:] # Clear the list
# clear() — remove all elements
items = [1, 2, 3, 4, 5]
items.clear()
print(items) # []
Searching
| Method | Description | Time Complexity |
|---|---|---|
index(x) | Return index of first occurrence of x | O(n) |
count(x) | Count occurrences of x | O(n) |
x in lst | Check if x is in the list | O(n) |
python
# index() — find position of an element
primes = [2, 3, 5, 7, 11, 13, 17]
print(primes.index(7)) # 3
print(primes.index(13)) # 5
# count() — how many times does a value appear?
data = [1, 2, 2, 3, 2, 4, 2]
print(data.count(2)) # 4
print(data.count(5)) # 0
# The 'in' operator
fruits = ["apple", "banana", "cherry"]
print("banana" in fruits) # True
print("grape" in fruits) # False
Sorting
| Method/Function | Description | Returns |
|---|---|---|
lst.sort() | Sort list in-place | None |
sorted(lst) | Return new sorted list | New list |
lst.sort(key=fn) | Sort with custom key | None |
lst.sort(reverse=True) | Sort descending | None |
reversed(lst) | Return reverse iterator | Iterator |
lst.reverse() | Reverse in-place | None |
python
# sort() — in-place sorting
scores = [85, 92, 78, 95, 88]
scores.sort()
print(scores) # [78, 85, 88, 92, 95]
scores.sort(reverse=True)
print(scores) # [95, 92, 88, 85, 78]
# sorted() — returns a new sorted list
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
new_sorted = sorted(numbers)
print(numbers) # [3, 1, 4, 1, 5, 9, 2, 6] — unchanged
print(new_sorted) # [1, 1, 2, 3, 4, 5, 6, 9]
# Sorting with a key function
words = ["banana", "apple", "cherry", "date"]
words.sort(key=len) # Sort by string length
print(words) # ['date', 'apple', 'banana', 'cherry']
# Case-insensitive sorting
names = ["charlie", "Alice", "bob"]
names.sort(key=str.lower)
print(names) # ['Alice', 'bob', 'charlie']
# Sort by specific attribute
students = [("Alice", 85), ("Bob", 92), ("Charlie", 78)]
students.sort(key=lambda student: student[1])
print(students) # [('Charlie', 78), ('Alice', 85), ('Bob', 92)]
# reversed() — returns an iterator
nums = [1, 2, 3]
rev = reversed(nums)
print(list(rev)) # [3, 2, 1]
print(nums) # [1, 2, 3] — unchanged
# reverse() — in-place reversal
nums = [1, 2, 3]
nums.reverse()
print(nums) # [3, 2, 1]
Other Methods
python
# copy() — create a shallow copy
original = [1, 2, 3, [4, 5]]
copied = original.copy()
copied[0] = 99
print(original) # [1, 2, 3, [4, 5]] — unchanged
# But shallow copy has caveats with nested objects
copied[3][0] = 99
print(original) # [1, 2, 3, [99, 5]] — nested list is shared!
Related Built-In Functions
len() — Number of Elements
python
numbers = [4, 2, 7, 1, 9, 3]
print(len(numbers)) # 6
words = ["apple", "banana", "cherry"]
print(len(words)) # 3
print(len([])) # 0
min() and max() — Minimum and Maximum
python
numbers = [4, 2, 7, 1, 9, 3]
print(min(numbers)) # 1
print(max(numbers)) # 9
# With strings (lexicographic)
words = ["apple", "banana", "cherry"]
print(min(words)) # 'apple'
print(max(words)) # 'cherry'
# With key function
students = [("Alice", 85), ("Bob", 92), ("Charlie", 78)]
print(min(students, key=lambda s: s[1])) # ('Charlie', 78)
print(max(students, key=lambda s: s[1])) # ('Bob', 92)
sum() — Sum of Elements
python
numbers = [1, 2, 3, 4, 5]
print(sum(numbers)) # 15
# With start value
print(sum(numbers, 10)) # 25
# Sum of booleans (True=1, False=0)
booleans = [True, False, True, True, False]
print(sum(booleans)) # 3
sorted() — Return New Sorted List
python
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
print(sorted(numbers)) # [1, 1, 2, 3, 4, 5, 6, 9]
# With key
words = ["banana", "apple", "cherry"]
print(sorted(words, key=len)) # ['apple', 'banana', 'cherry']
# With reverse
print(sorted(numbers, reverse=True)) # [9, 6, 5, 4, 3, 2, 1, 1]
reversed() — Return Reverse Iterator
python
numbers = [1, 2, 3, 4, 5]
rev = reversed(numbers)
print(list(rev)) # [5, 4, 3, 2, 1]
print(numbers) # [1, 2, 3, 4, 5] — unchanged
# Useful in for loops
for num in reversed([1, 2, 3, 4, 5]):
print(num, end=" ")
# 5 4 3 2 1
enumerate() — Index and Value Pairs
python
fruits = ["apple", "banana", "cherry"]
for index, fruit in enumerate(fruits):
print(f"{index}: {fruit}")
# 0: apple
# 1: banana
# 2: cherry
# With custom start
for index, fruit in enumerate(fruits, start=1):
print(f"{index}. {fruit}")
# 1. apple
# 2. banana
# 3. cherry
zip() — Combine Multiple Iterables
python
names = ["Alice", "Bob", "Charlie"]
scores = [85, 92, 78]
grades = ["A", "A+", "B+"]
# Zip two lists
for name, score in zip(names, scores):
print(f"{name}: {score}")
# Zip three lists
for name, score, grade in zip(names, scores, grades):
print(f"{name}: {score} ({grade})")
# Convert to list of tuples
pairs = list(zip(names, scores))
print(pairs) # [('Alice', 85), ('Bob', 92), ('Charlie', 78)]
map() — Apply Function to Each Element
python
numbers = [1, 2, 3, 4, 5]
# Square each number
squared = list(map(lambda x: x**2, numbers))
print(squared) # [1, 4, 9, 16, 25]
# With multiple iterables
a = [1, 2, 3]
b = [10, 20, 30]
sums = list(map(lambda x, y: x + y, a, b))
print(sums) # [11, 22, 33]
# Convert strings to integers
str_nums = ["1", "2", "3", "4", "5"]
int_nums = list(map(int, str_nums))
print(int_nums) # [1, 2, 3, 4, 5]
filter() — Keep Elements Where Function Returns True
python
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# Keep even numbers
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens) # [2, 4, 6, 8, 10]
# Keep strings longer than 3 characters
words = ["hi", "hello", "hey", "howdy", "yo"]
long_words = list(filter(lambda w: len(w) > 3, words))
print(long_words) # ['hello', 'howdy']
# Filter out None values
data = [1, None, 2, None, 3]
clean = list(filter(None, data))
print(clean) # [1, 2, 3]
any() and all() — Boolean Checks
python
numbers = [0, 1, 2, 3, 4]
print(any(numbers)) # True — at least one truthy value
print(all(numbers)) # False — not all truthy (0 is falsy)
numbers = [1, 2, 3, 4, 5]
print(all(numbers)) # True — all truthy
# With conditions
words = ["hello", "world", "python"]
print(all(len(w) > 3 for w in words)) # True
print(any(len(w) > 10 for w in words)) # False
# Check if any element meets condition
students = [("Alice", 85), ("Bob", 92), ("Charlie", 78)]
print(any(score >= 90 for _, score in students)) # True
print(all(score >= 90 for _, score in students)) # False
List Comprehensions
Basic Syntax
python
squares = [x**2 for x in range(10)]
print(squares) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Filtering with Conditions
python
evens = [x for x in range(20) if x % 2 == 0]
print(evens) # [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
long_words = [word.upper() for word in ["hi", "hello", "hey", "howdy"] if len(word) > 2]
print(long_words) # ['HELLO', 'HOWDY']
Nested Comprehensions
python
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [num for row in matrix for num in row]
print(flat) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
identity = [[1 if i == j else 0 for j in range(3)] for i in range(3)]
print(identity) # [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
Conditional Expressions (Ternary)
python
labels = ["even" if x % 2 == 0 else "odd" for x in range(5)]
print(labels) # ['even', 'odd', 'even', 'odd', 'even']
Nested Lists
python
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
print(matrix[0][0]) # 1
print(matrix[1][2]) # 6
# Matrix addition
A = [[1, 2], [3, 4]]
B = [[5, 6], [7, 8]]
C = [[A[i][j] + B[i][j] for j in range(2)] for i in range(2)]
print(C) # [[6, 8], [10, 12]]
# Transpose
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
transposed = [[row[i] for row in matrix] for i in range(3)]
print(transposed) # [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
Deep Copy vs Shallow Copy
python
import copy
original = [[1, 2, 3], [4, 5, 6]]
# Shallow copy — nested objects are shared
shallow = original.copy()
shallow[0][0] = 99
print(original[0][0]) # 99 — original is affected!
# Deep copy — everything is copied
original = [[1, 2, 3], [4, 5, 6]]
deep = copy.deepcopy(original)
deep[0][0] = 99
print(original[0][0]) # 1 — original is safe
Lists vs Other Sequences
Lists vs Tuples
| Feature | List [] | Tuple () |
|---|---|---|
| Mutable | Yes | No |
| Syntax | [1, 2, 3] | (1, 2, 3) |
| Performance | Slightly slower | Slightly faster |
| Use case | Dynamic collections | Fixed records |
| Dictionary key | No | Yes (if hashable) |
Lists vs Generators
| Feature | List | Generator |
|---|---|---|
| Memory | Stores all elements | Produces on demand |
| Iteration | Multiple times | Single pass |
| Indexing | Supports lst[i] | No random access |
python
import sys
# List — all elements stored in memory
squares_list = [x**2 for x in range(1_000_000)]
# Generator — produces values on demand
squares_gen = (x**2 for x in range(1_000_000))
print(sys.getsizeof(squares_list)) # ~8,448,728 bytes
print(sys.getsizeof(squares_gen)) # ~208 bytes
Common Patterns
Unpacking
python
first, second, *rest = [1, 2, 3, 4, 5]
print(first) # 1
print(rest) # [3, 4, 5]
first, _, third, *_ = [1, 2, 3, 4, 5]
print(third) # 3
# Unpacking in function arguments
def greet(name, age):
return f"{name} is {age} years old"
person = ["Alice", 30]
print(greet(*person)) # "Alice is 30 years old"
Removing Duplicates (Preserving Order)
python
def remove_duplicates(lst):
seen = set()
result = []
for item in lst:
if item not in seen:
seen.add(item)
result.append(item)
return result
data = [3, 1, 2, 3, 1, 4, 2, 5]
print(remove_duplicates(data)) # [3, 1, 2, 4, 5]
# Simpler approach (Python 3.7+)
unique = list(dict.fromkeys(data))
print(unique) # [3, 1, 2, 4, 5]
Flattening Nested Lists
python
nested = [[1, 2, 3], [4, 5], [6, 7, 8, 9]]
flat = [item for sublist in nested for item in sublist]
print(flat) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
from itertools import chain
flat = list(chain.from_iterable(nested))
print(flat) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
Performance Considerations
| Operation | Time Complexity | Notes |
|---|---|---|
append(x) | O(1) amortized | Fast |
pop() (from end) | O(1) | Fast |
pop(0) (from start) | O(n) | Slow — all elements shift |
insert(0, x) | O(n) | Slow — all elements shift |
x in lst | O(n) | Linear search |
lst[i] | O(1) | Random access is fast |
len(lst) | O(1) | Length is cached |
sort() | O(n log n) | Timsort algorithm |
copy() | O(n) | Must copy all elements |
When to Use collections.deque
python
from collections import deque
# deque is optimized for append/popleft operations
dq = deque([1, 2, 3])
dq.appendleft(0) # O(1) — fast regardless of size
# List — O(n) for insert at start
lst = [1, 2, 3]
lst.insert(0, 0) # Slow for large lists
Common Mistakes
1. Mutable Default Arguments
python
# WRONG
def add_item(item, lst=[]):
lst.append(item)
return lst
print(add_item(1)) # [1]
print(add_item(2)) # [1, 2] — unexpected!
# CORRECT
def add_item(item, lst=None):
if lst is None:
lst = []
lst.append(item)
return lst
print(add_item(1)) # [1]
print(add_item(2)) # [2]
2. Modifying While Iterating
python
# WRONG
numbers = [1, 2, 3, 4, 5, 6]
for num in numbers:
if num % 2 == 0:
numbers.remove(num)
# CORRECT
numbers = [1, 2, 3, 4, 5, 6]
numbers = [num for num in numbers if num % 2 != 0]
3. == vs is for Lists
python
a = [1, 2, 3]
b = [1, 2, 3]
c = a
print(a == b) # True — same values
print(a is b) # False — different objects
print(a is c) # True — same object
4. Forgetting sort() Returns None
python
numbers = [3, 1, 4, 1, 5]
result = numbers.sort()
print(result) # None!
# Use sorted() to get a new list
sorted_numbers = sorted(numbers)
Practice Exercises
Exercise 1: Remove Duplicates
python
def remove_duplicates(lst):
seen = set()
result = []
for item in lst:
if item not in seen:
seen.add(item)
result.append(item)
return result
print(remove_duplicates([1, 2, 2, 3, 4, 4, 5])) # [1, 2, 3, 4, 5]
print(remove_duplicates(["a", "b", "a", "c", "b"])) # ['a', 'b', 'c']
Exercise 2: Rotate a List
python
def rotate_right(lst, k):
if not lst:
return lst
k = k % len(lst)
return lst[-k:] + lst[:-k]
print(rotate_right([1, 2, 3, 4, 5], 2)) # [4, 5, 1, 2, 3]
print(rotate_right([1, 2, 3, 4, 5], 7)) # [4, 5, 1, 2, 3]
Exercise 3: Find Common Elements
python
def common_elements(lst1, lst2):
return list(set(lst1) & set(lst2))
print(common_elements([1, 2, 3, 4, 5], [3, 4, 5, 6, 7])) # [3, 4, 5]
print(common_elements(["a", "b", "c"], ["b", "c", "d"])) # ['b', 'c']
Key Takeaways
- Lists are ordered, mutable, and can hold any data type — Python's most versatile collection
- Use literal syntax
[]— faster and more Pythonic thanlist() append()vsextend()— append adds one element, extend adds multiplesort()modifies in-place,sorted()returns a new list — choose based on whether you need to preserve the original- List comprehensions are concise and fast — use them for simple transformations and filters
- Watch out for shallow copies with nested lists — use
copy.deepcopy()when needed - Avoid mutable default arguments — always use
Noneas the default - Consider performance —
insert(0, x)is O(n), usedequefor frequent start insertions - Use
infor membership testing — but remember it's O(n) for lists - Built-in functions like
len(),min(),max(),sum(),sorted(),enumerate(),zip(),map(),filter(),any(),all()make list operations elegant and efficient