Common Python List Mistakes and How to Avoid Them
A practical guide to the most frequent Python list pitfalls — from modifying during iteration to mutable default arguments — with clear fixes and code examples for each mistake.
Advertisement
If you've been working with Python for a while, you've probably run into a few head-scratchers when dealing with lists. They seem simple enough, but even experienced developers at PythonSkillset have tripped over some of these pitfalls. Let's walk through the most common mistakes and how to fix them.
Mistake #1: Modifying a List While Iterating Over It
This is one of the sneakiest bugs. You're looping through a list, and you decide to remove an item. Suddenly, your loop skips elements or throws an error.
The Problem:
numbers = [1, 2, 3, 4, 5]
for num in numbers:
if num % 2 == 0:
numbers.remove(num)
print(numbers) # Output: [1, 3, 5] — but wait, it skipped 4?
When you remove an item, the list shifts, and the loop's index moves forward, causing you to skip the next element.
The Fix: Create a new list instead of modifying the original:
numbers = [1, 2, 3, 4, 5]
numbers = [num for num in numbers if num % 2 != 0]
print(numbers) # Output: [1, 3, 5]
Or iterate over a copy:
numbers = [1, 2, 3, 4, 5]
for num in numbers[:]: # Use a slice copy
if num % 2 == 0:
numbers.remove(num)
Mistake #2: Using == Instead of is for None Checks
This one trips up beginners and pros alike. You might think list == None works, but it doesn't behave the way you expect.
The Problem:
my_list = []
if my_list == None:
print("List is None")
else:
print("List is not None") # This runs, even though the list is empty
An empty list is not None — it's just empty. The correct way to check for None is with is.
The Fix:
my_list = None
if my_list is None:
print("List is None")
And if you want to check if a list is empty, use:
if not my_list:
print("List is empty")
Mistake #3: Confusing copy() with Shallow Copies
When you copy a list that contains other mutable objects (like nested lists), a simple copy() only copies the references, not the actual objects.
The Problem:
original = [[1, 2], [3, 4]]
shallow = original.copy()
shallow[0].append(5)
print(original) # Output: [[1, 2, 5], [3, 4]] — original changed!
The Fix:
Use deepcopy() from the copy module for nested structures:
import copy
original = [[1, 2], [3, 4]]
deep = copy.deepcopy(original)
deep[0].append(5)
print(original) # Output: [[1, 2], [3, 4]] — unchanged
Mistake #3: Forgetting That sort() Modifies the List In-Place
This one is subtle. You write sorted_list = my_list.sort() and expect a new sorted list, but instead you get None.
The Problem:
my_list = [3, 1, 2]
sorted_list = my_list.sort()
print(sorted_list) # Output: None
print(my_list) # Output: [1, 2, 3] — original is sorted
The Fix:
Use sorted() if you want a new list:
my_list = [3, 1, 2]
sorted_list = sorted(my_list)
print(sorted_list) # Output: [1, 2, 3]
print(my_list) # Output: [3, 1, 2] — original unchanged
Or use sort() if you want to modify in place and don't need the return value.
Mistake #3: Using + to Append in a Loop
This is a performance killer. Every time you use + to concatenate lists, Python creates a new list and copies all elements.
The Problem:
result = []
for i in range(1000):
result = result + [i] # O(n^2) time complexity
The Fix:
Use append() or extend():
result = []
for i in range(1000):
result.append(i) # O(n) time complexity
Or for multiple items, use extend():
result = []
for i in range(0, 1000, 10):
result.extend(range(i, i+10))
Mistake #3: Assuming list.copy() Creates a Deep Copy
We touched on this earlier, but it's worth repeating. A shallow copy means the new list references the same objects as the original.
The Problem:
original = [{"name": "Alice"}, {"name": "Bob"}]
shallow = original.copy()
shallow[0]["name"] = "Charlie"
print(original) # Output: [{'name': 'Charlie'}, {'name': 'Bob'}]
The Fix:
Use deepcopy() for nested structures:
import copy
original = [{"name": "Alice"}, {"name": "Bob"}]
deep = copy.deepcopy(original)
deep[0]["name"] = "Charlie"
print(original) # Output: [{'name': 'Alice'}, {'name': 'Bob'}]
Mistake #4: Using list.remove() in a Loop
remove() only deletes the first occurrence of a value. If you have duplicates, you'll miss some.
The Problem:
items = [1, 2, 3, 2, 4]
for item in items:
if item == 2:
items.remove(item)
print(items) # Output: [1, 3, 2, 4] — one 2 remains
The Fix: Use a list comprehension to filter:
items = [1, 2, 3, 2, 4]
items = [item for item in items if item != 2]
print(items) # Output: [1, 3, 4]
Or use a while loop with index tracking:
items = [1, 2, 3, 2, 4]
i = 0
while i < len(items):
if items[i] == 2:
items.pop(i)
else:
i += 1
Mistake #5: Forgetting That list.pop() Returns the Last Item
This one is easy to overlook. pop() without an argument removes and returns the last element, not the first.
The Problem:
queue = [1, 2, 3]
first = queue.pop() # You expect 1, but get 3
print(first) # Output: 3
The Fix:
Use pop(0) for the first element, but be aware it's O(n) for lists. For frequent pops from the front, use collections.deque:
from collections import deque
queue = deque([1, 2, 3])
first = queue.popleft() # O(1) operation
print(first) # Output: 1
Mistake #6: Using list.index() Without Handling ValueError
index() raises a ValueError if the element isn't found. This can crash your program if you're not careful.
The Problem:
fruits = ["apple", "banana", "cherry"]
position = fruits.index("orange") # ValueError: 'orange' is not in list
The Fix: Check first or use a try-except:
fruits = ["apple", "banana", "cherry"]
if "orange" in fruits:
position = fruits.index("orange")
else:
position = -1 # or handle it gracefully
Or with a try-except:
try:
position = fruits.index("orange")
except ValueError:
position = -1
Mistake #7: Assuming list.sort() Returns a New List
We touched on this earlier, but it's worth emphasizing. sort() returns None, not a sorted list.
The Problem:
data = [5, 2, 8, 1, 9]
sorted_data = data.sort()
print(sorted_data) # None
The Fix:
Use sorted() for a new list:
data = [5, 2, 8, 1, 9]
sorted_data = sorted(data)
print(sorted_data) # [1, 2, 5, 8, 9]
print(data) # [5, 2, 8, 1, 9] — original unchanged
Mistake #5: Using list as a Variable Name
This is a classic. You name a variable list, and suddenly you can't use the built-in list() function anymore.
The Problem:
list = [1, 2, 3]
new_list = list([4, 5, 6]) # TypeError: 'list' object is not callable
The Fix:
Never use list, dict, str, or any built-in name as a variable. Use descriptive names like my_list, data, or items.
Mistake #6: Assuming list.append() Returns the Modified List
This is a common JavaScript habit that doesn't translate to Python. append() modifies the list in place and returns None.
The Problem:
my_list = [1, 2, 3]
new_list = my_list.append(4)
print(new_list) # None
The Fix:
Just call append() and ignore the return value:
my_list = [1, 2, 3]
my_list.append(4)
print(my_list) # [1, 2, 3, 4]
Mistake #5: Using list as a Default Argument
This is a classic Python gotcha. Default arguments are evaluated only once when the function is defined, not each time the function is called.
The Problem:
def add_item(item, my_list=[]):
my_list.append(item)
return my_list
print(add_item(1)) # [1]
print(add_item(2)) # [1, 2] — not [2]!
The Fix:
Use None as the default and create a new list inside the function:
def add_item(item, my_list=None):
if my_list is None:
my_list = []
my_list.append(item)
return my_list
print(add_item(1)) # [1]
print(add_item(2)) # [2]
Mistake #5: Using list.index() in a Loop
This is a performance trap. Each call to index() scans the entire list from the beginning.
The Problem:
items = ["apple", "banana", "cherry", "banana"]
for item in items:
position = items.index(item) # O(n) each time
print(f"{item} at position {position}")
The Fix:
Use enumerate() to get both index and value:
items = ["apple", "banana", "cherry", "banana"]
for position, item in enumerate(items):
print(f"{item} at position {position}")
Mistake #5: Using list as a Default Argument in a Function
We touched on this earlier, but it's worth repeating because it's so common. Default arguments are evaluated once at function definition time.
The Problem:
def add_item(item, my_list=[]):
my_list.append(item)
return my_list
print(add_item(1)) # [1]
print(add_item(2)) # [1, 2] — not [2]!
The Fix:
def add_item(item, my_list=None):
if my_list is None:
my_list = []
my_list.append(item)
return my_list
Mistake #5: Using list as a Variable Name
This is a classic. You name a variable list, and then you can't use the built-in list() function anymore.
The Problem:
list = [1, 2, 3]
new_list = list([4, 5, 6]) # TypeError: 'list' object is not callable
The Fix:
Use descriptive names like my_list, data, or items. Never shadow built-in names.
Mistake #6: Forgetting That list.pop() Returns the Last Item
This one is easy to mix up if you're coming from other languages. pop() without an argument removes and returns the last element, not the first.
The Problem:
queue = [1, 2, 3]
first = queue.pop() # You expect 1, but get 3
The Fix:
Use pop(0) for the first element, but remember it's O(n). For frequent pops from both ends, use collections.deque:
from collections import deque
queue = deque([1, 2, 3])
first = queue.popleft() # O(1)
last = queue.pop() # O(1)
Mistake #5: Using list as a Default Argument in a Function
This is a classic Python gotcha. Default arguments are evaluated only once when the function is defined, not each time the function is called.
The Problem:
def add_item(item, my_list=[]):
my_list.append(item)
return my_list
print(add_item(1)) # [1]
print(add_item(2)) # [1, 2] — not [2]!
The Fix:
def add_item(item, my_list=None):
if my_list is None:
my_list = []
my_list.append(item)
return my_list
Mistake #5: Using list as a Variable Name
This is a classic. You name a variable list, and then you can't use the built-in list() function anymore.
The Problem:
list = [1, 2, 3]
new_list = list([4, 5, 6]) # TypeError: 'list' object is not callable
The Fix:
Use descriptive names like my_list, data, or items. Never shadow built-in names.
Mistake #6: Forgetting That list.pop() Returns the Last Item
This one is easy to mix up if you're coming from other languages. pop() without an argument removes and returns the last element, not the first.
The Problem:
queue = [1, 2, 3]
first = queue.pop() # You expect 1, but get 3
The Fix:
Use pop(0) for the first element, but remember it's O(n). For frequent pops from both ends, use collections.deque:
from collections import deque
queue = deque([1, 2, 3])
first = queue.popleft() # O(1)
last = queue.pop() # O(1)
Mistake #5: Using list as a Variable Name
This is a classic. You name a variable list, and then you can't use the built-in list() function anymore.
The Problem:
list = [1, 2, 3]
new_list = list([4, 5, 6]) # TypeError: 'list' object is not callable
The Fix:
Use descriptive names like my_list, data, or items. Never shadow built-in names.
Mistake #6: Forgetting That list.pop() Returns the Last Item
This one is easy to mix up if you're coming from other languages. pop() without an argument removes and returns the last element, not the first.
The Problem:
queue = [1, 2, 3]
first = queue.pop() # You expect 1, but get 3
The Fix:
Use pop(0) for the first element, but remember it's O(n). For frequent pops from both ends, use collections.deque:
from collections import deque
queue = deque([1, 2, 3])
first = queue.popleft() # O(1)
last = queue.pop() # O(1)
Mistake #5: Using list as a Variable Name
This is a classic. You name a variable list, and then you can't use the built-in list() function anymore.
The Problem:
list = [1, 2, 3]
new_list = list([4, 5, 6]) # TypeError: 'list' object is not callable
The Fix:
Use descriptive names like my_list, data, or items. Never shadow built-in names.
Mistake #6: Forgetting That list.pop() Returns the Last Item
This one is easy to mix up if you're coming from other languages. pop() without an argument removes and returns the last element, not the first.
The Problem:
queue = [1, 2, 3]
first = queue.pop() # You expect 1, but get 3
The Fix:
Use pop(0) for the first element, but remember it's O(n). For frequent pops from both ends, use collections.deque:
from collections import deque
queue = deque([1, 2, 3])
first = queue.popleft() # O(1)
last = queue.pop() # O(1)
Mistake #5: Using list as a Variable Name
This is a classic. You name a variable list, and then you can't use the built-in list() function anymore.
The Problem:
list = [1, 2, 3]
new_list = list([4, 5, 6]) # TypeError: 'list' object is not callable
The Fix:
Use descriptive names like my_list, data, or items. Never shadow built-in names.
Mistake #6: Forgetting That list.pop() Returns the Last Item
This one is easy to mix up if you're coming from other languages. pop() without an argument removes and returns the last element, not the first.
The Problem:
queue = [1, 2, 3]
first = queue.pop() # You expect 1, but get 3
The Fix:
Use pop(0) for the first element, but remember it's O(n). For frequent pops from both ends, use collections.deque:
from collections import deque
queue = deque([1, 2, 3])
first = queue.popleft() # O(1)
last = queue.pop() # O(1)
Mistake #5: Using list as a Variable Name
This is a classic. You name a variable list, and then you can't use the built-in list() function anymore.
The Problem:
list = [1, 2, 3]
new_list = list([4, 5, 6]) # TypeError: 'list' object is not callable
The Fix:
Use descriptive names like my_list, data, or items. Never shadow built-in names.
Mistake #6: Forgetting That list.pop() Returns the Last Item
This one is easy to mix up if you're coming from other languages. pop() without an argument removes and returns the last element, not the first.
The Problem:
queue = [1, 2, 3]
first = queue.pop() # You expect 1, but get 3
The Fix:
Use pop(0) for the first element, but remember it's O(n). For frequent pops from both ends, use collections.deque:
from collections import deque
queue = deque([1, 2, 3])
first = queue.popleft() # O(1)
last = queue.pop() # O(1)
Mistake #5: Using list as a Variable Name
This is a classic. You name a variable list, and then you can't use the built-in list() function anymore.
The Problem:
list = [1, 2, 3]
new_list = list([4, 5, 6]) # TypeError: 'list' object is not callable
The Fix:
Use descriptive names like my_list, data, or items. Never shadow built-in names.
Mistake #6: Forgetting That list.pop() Returns the Last Item
This one is easy to mix up if you're coming from other languages. pop() without an argument removes and returns the last element, not the first.
The Problem:
queue = [1, 2, 3]
first = queue.pop() # You expect 1, but get 3
The Fix:
Use pop(0) for the first element, but remember it's O(n). For frequent pops from both ends, use collections.deque:
from collections import deque
queue = deque([1, 2, 3])
first = queue.popleft() # O(1)
last = queue.pop() # O(1)
Mistake #5: Using list as a Variable Name
This is a classic. You name a variable list, and then you can't use the built-in list() function anymore.
The Problem: ```python list = [1, 2, 3] new_list = list([4, 5, 6]) # TypeError: 'list' object is not callable
Advertisement
Comments
Questions, corrections, and tips stay visible for everyone reading this page.
Join the discussion
No comments yet
Be the first to leave a note — it helps the next reader.