Python Performance Optimization
Writing efficient Python code involves choosing the right data structures, avoiding unnecessary work, and measuring performance before optimizing.
Measure Before Optimizing
"Premature optimization is the root of all evil." Always profile your code first to find actual bottlenecks using tools like the time module, timeit, or the cProfile module — don't guess.
Choosing the Right Data Structure
Use the right tool for the job: lists for ordered collections, sets for fast membership testing (in is O(1) for sets vs O(n) for lists), dictionaries for key-based lookups, and tuples for fixed, immutable data.
Avoiding Unnecessary Work
Avoid repeated computation inside loops — calculate values once outside the loop if they don't change. Avoid building large intermediate lists when a generator would do.
List Comprehensions vs Loops
List comprehensions are often faster than equivalent for-loops with .append() because they are optimized internally by the interpreter.
String Concatenation
Repeatedly concatenating strings with + in a loop is slow because strings are immutable (each concatenation creates a new string). Use "".join(list_of_strings) instead.
Using Built-in Functions and Libraries
Built-in functions (sum(), max(), sorted()) and libraries like NumPy are implemented in C and are usually much faster than hand-written Python loops for the same task.
Caching with functools.lru_cache
For expensive functions called repeatedly with the same arguments, @functools.lru_cache caches results automatically, avoiding redundant computation.