15. Top Pitfalls

Common mistakes that cause subtle bugs—recognize and avoid them quickly.

  • Mutable default arguments

    Bad creates shared state across calls; use None and create inside.

    def add_item(x, bag=[]):  # BAD
        bag.append(x)
        return bag
    
    def add_item_safe(x, bag=None):  # GOOD
        if bag is None:
            bag = []
        bag.append(x)
        return bag
    
  • List multiplication with inner lists

    grid = [[0]*3]*3      # BAD: rows share the same list
    grid[0][0] = 1        # mutates all rows
    grid = [[0 for _ in range(3)] for _ in range(3)]  # GOOD
    
  • Floating point equality

    0.1 + 0.2 == 0.3      # False
    import math
    math.isclose(0.1 + 0.2, 0.3, rel_tol=1e-9)  # True
    
  • Late binding in closures (loop variables)

    funcs = [lambda i=i: i*i for i in range(3)]  # default arg captures value
    [f() for f in funcs]  # [0, 1, 4]
    
  • Shadowing built-ins

    Avoid names like list, dict, id, type for your variables.

  • Overbroad exception handling

    try:
        risky()
    except Exception:       # BAD: hides bugs
        pass
    
    try:
        risky()
    except (ValueError, KeyError) as e:  # GOOD: handle expected cases
        handle(e)
    
  • File encodings/newlines

    Always set encoding="utf-8". For CSV on Windows, pass newline="" to avoid blank lines.

  • Off-by-one slicing

    Remember end index is exclusive: s[a:b] includes a, excludes b.

  • Using is for value comparison

    Use == for equality, is only for identity (None, singletons).

  • HTTP without timeouts

    requests.get(url, timeout=5)  # always set a timeout