Skip to content

Latest commit

 

History

History
357 lines (265 loc) · 6.83 KB

File metadata and controls

357 lines (265 loc) · 6.83 KB

Common Python Pitfalls & How to Avoid Them 🚨

This guide covers common mistakes beginners (and sometimes experienced developers) make in Python.

1. Mutable Default Arguments

❌ WRONG:

def add_item(item, my_list=[]):
    my_list.append(item)
    return my_list

print(add_item(1))  # [1]
print(add_item(2))  # [1, 2] - Oops! Previous items still there!

✅ CORRECT:

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] - Correct!

Why? Default arguments are evaluated once when the function is defined, not each time it's called.


2. Modifying a List While Iterating

❌ WRONG:

numbers = [1, 2, 3, 4, 5]
for num in numbers:
    if num % 2 == 0:
        numbers.remove(num)  # Dangerous!
print(numbers)  # Might not remove all even numbers

✅ CORRECT:

numbers = [1, 2, 3, 4, 5]
numbers = [num for num in numbers if num % 2 != 0]  # List comprehension
# OR
numbers = list(filter(lambda x: x % 2 != 0, numbers))  # Filter
print(numbers)  # [1, 3, 5]

Why? Modifying a list while iterating can skip elements or cause unexpected behavior.


3. Using == Instead of is for None

❌ WRONG (usually):

if value == None:  # Works but not Pythonic
    pass

✅ CORRECT:

if value is None:  # Pythonic way
    pass

Why? is checks identity (same object), == checks equality (same value). For None, True, False, use is.


4. String Concatenation in Loops

❌ WRONG (slow):

result = ""
for i in range(1000):
    result += str(i)  # Creates new string each time!

✅ CORRECT (fast):

result = "".join(str(i) for i in range(1000))

Why? Strings are immutable. Each += creates a new string object. .join() is much more efficient.


5. Forgetting to Close Files (Old Style)

❌ WRONG:

file = open("data.txt", "r")
content = file.read()
# What if an error occurs? File might not close!
file.close()

✅ CORRECT:

with open("data.txt", "r") as file:
    content = file.read()
# File automatically closes, even if error occurs

Why? Context managers (with statement) ensure resources are properly cleaned up.


6. Confusing is and ==

❌ CONFUSION:

a = [1, 2, 3]
b = [1, 2, 3]
print(a == b)  # True (same values)
print(a is b)  # False (different objects)

Understanding:

  • == compares values
  • is compares identity (same object in memory)

Use is for: None, True, False
Use == for: Everything else


7. Variable Scope Confusion

❌ WRONG:

x = 10

def change_x():
    x = 20  # Creates local variable, doesn't change global x

change_x()
print(x)  # Still 10!

✅ CORRECT:

x = 10

def change_x():
    global x  # Declare we want to modify global x
    x = 20

change_x()
print(x)  # Now 20

Why? Python assumes variables are local unless you use global or nonlocal.


8. Integer Division Confusion

❌ CONFUSION:

result = 5 / 2
print(result)  # 2.5 (float)

result = 5 // 2
print(result)  # 2 (integer division)

Understanding:

  • / always returns a float (Python 3)
  • // returns integer (floor division)

9. Shallow Copy vs Deep Copy

❌ WRONG:

original = [[1, 2], [3, 4]]
copy = original.copy()  # Shallow copy
copy[0].append(5)
print(original)  # [[1, 2, 5], [3, 4]] - Original changed!

✅ CORRECT:

import copy

original = [[1, 2], [3, 4]]
deep_copy = copy.deepcopy(original)  # Deep copy
deep_copy[0].append(5)
print(original)  # [[1, 2], [3, 4]] - Original unchanged

Why? Shallow copy only copies the outer list, not nested objects.


10. Forgetting Return Statement

❌ WRONG:

def add(a, b):
    a + b  # Forgot return!

result = add(5, 3)
print(result)  # None

✅ CORRECT:

def add(a, b):
    return a + b

result = add(5, 3)
print(result)  # 8

11. Index Out of Range

❌ WRONG:

my_list = [1, 2, 3]
print(my_list[5])  # IndexError!

✅ CORRECT:

my_list = [1, 2, 3]
if len(my_list) > 5:
    print(my_list[5])
else:
    print("Index out of range")

# OR use try/except
try:
    print(my_list[5])
except IndexError:
    print("Index out of range")

12. Dictionary KeyError

❌ WRONG:

user = {"name": "Alice"}
print(user["age"])  # KeyError!

✅ CORRECT:

user = {"name": "Alice"}
print(user.get("age", "Unknown"))  # Returns "Unknown" if key doesn't exist

# OR check first
if "age" in user:
    print(user["age"])

13. Type Confusion with input()

❌ WRONG:

age = input("Enter your age: ")
if age >= 18:  # TypeError! Can't compare string with int
    print("Adult")

✅ CORRECT:

age = int(input("Enter your age: "))  # Convert to int
if age >= 18:
    print("Adult")

Why? input() always returns a string, even for numbers.


14. List vs Tuple Confusion

❌ WRONG:

coordinates = [10, 20]  # List (mutable)
coordinates[0] = 5  # Works, but shouldn't for coordinates

# Better for immutable data:
coordinates = (10, 20)  # Tuple (immutable)
coordinates[0] = 5  # TypeError - can't modify tuple

Understanding:

  • Use lists when data needs to change
  • Use tuples for fixed data (coordinates, constants, dictionary keys)

15. Not Using enumerate() When You Need Index

❌ WRONG:

items = ["apple", "banana", "cherry"]
i = 0
for item in items:
    print(f"{i}: {item}")
    i += 1  # Manual counter

✅ CORRECT:

items = ["apple", "banana", "cherry"]
for i, item in enumerate(items):
    print(f"{i}: {item}")

💡 Key Takeaways

  1. Always use with for file operations
  2. Use is None not == None
  3. Avoid mutable default arguments
  4. Don't modify lists while iterating
  5. Use .join() for string concatenation in loops
  6. Understand the difference between is and ==
  7. Convert input() to the right type
  8. Use enumerate() when you need index + value
  9. Use .get() for dictionary access with defaults
  10. Always use return in functions that should return values

🔍 How to Debug

  1. Read error messages carefully - They tell you what went wrong
  2. Use print() statements to see variable values
  3. Use a debugger (VS Code has built-in debugging)
  4. Check types with type() function
  5. Test small pieces of code separately

📚 Further Reading