Python’s slow performance is not due to being an “interpreted” language, but rather due to incorrect data structure choices, excessive unnecessary object creation, repeated function calls, and misuse of exceptions. There are only two categories of underlying principles:
- 1. Reduce time complexity
- 2. Reduce memory usage and object overhead
The following sections will elaborate on these two categories and demonstrate with minimal code “why it is indeed faster”.
1. Optimizations Based on Time Complexity
1. Use set instead of list for membership testing
Principle
<span>list</span> lookup is O(n), while <span>set</span> is a hash structure with an average of O(1).
Incorrect Implementation (Slow)
nums = list(range(100000))
print(50000 in nums) # O(n)
Correct Implementation (Much Faster)
nums_set = set(range(100000))
print(50000 in nums_set) # O(1)
Why Beginners Should Master This Immediately
This is necessary for all scenarios involving “deduplication”, “filtering”, and “checking existence”.
2. bisect: Use binary search instead of linear search in sorted lists
Principle
Linear search O(n) → Binary search O(log n).
Incorrect Implementation
data = list(range(100000))
data.index(99999) # O(n)
Correct Implementation
import bisect
data = list(range(100000))
pos = bisect.bisect_left(data, 99999) # O(log n)
Application Scenarios
Leaderboards, timelines, continuously inserting into sorted sequences.
3. Avoid calling immutable functions in loops
Principle
Python function calls are expensive. Repeatedly calling a function that returns a fixed value in a loop is pointless.
Incorrect Implementation
import math
for i in range(1000000):
x = math.pi # Repeated call
Correct Implementation
import math
pi = math.pi
for i in range(1000000):
x = pi
4. Encapsulate frequently used logic in local functions to reduce variable lookup costs
Principle
Python’s variable lookup order is: local > enclosed > global. Placing it inside a function is faster than placing it globally.
Incorrect Implementation
def add(x): # Global scope lookup is slower
return x + 1
for _ in range(1000000):
add(1)
Correct Implementation
def run():
def add_local(x): # Local lookup is faster
return x + 1
for _ in range(1000000):
add_local(1)
run()
5. Avoid using try/except for flow control (especially fatal in loops)
Principle
The overhead of exception handling is much higher than that of if statements.
Incorrect Implementation
lst = [1, 2, 3]
for i in range(100000):
try:
x = lst[5] # Always raises an exception
except IndexError:
pass
Correct Implementation
lst = [1, 2, 3]
for i in range(100000):
if len(lst) > 5:
x = lst[5]
6. Use C extension modules like math / itertools
Principle
C implementations are several times faster than Python code.
Example: math vs custom implementation
import math
math.sqrt(9) # Fast
9 ** 0.5 # Slow
Example: itertools instead of manual loops
import itertools
# All combinations
for a, b in itertools.combinations(range(5), 2):
pass
This is faster than manually writing two nested loops, does not create intermediate lists, and uses less memory.
2. Optimizations Based on Memory and Object Management
7. Avoid unnecessary list/dictionary copying
Incorrect Implementation
data = [i for i in range(100000)]
copy = data[:] # Unnecessary copy
Correct Implementation
data = [i for i in range(100000)]
# Directly use data
Principle
Copying = creating N new objects + new list memory allocation.
8. Preallocate lists when the size is known
Incorrect Implementation
arr = []
for i in range(100000):
arr.append(i) # Multiple expansions
Correct Implementation
arr = [0] * 100000 # Allocate all at once
for i in range(100000):
arr[i] = i
Applicable Scenarios
Large loops, data pipelines, machine learning preprocessing.
9. Use slots to optimize object memory
Principle
By default, class instances have a <span>__dict__</span>, which consumes a lot of memory.<span>__slots__</span> directly tells the interpreter “I only have these attributes”.
Example
class Point:
__slots__ = ('x', 'y') # Limit attributes
def __init__(self, x, y):
self.x = x
self.y = y
Significant savings when dealing with a large number of objects (over 100,000).
10. itertools’ lazy evaluation can avoid large memory structures
Incorrect Implementation (generating the entire list)
pairs = [(i, j) for i in range(1000) for j in range(1000)]
Correct Implementation (lazy, does not occupy memory)
import itertools
pairs = itertools.product(range(1000), range(1000))
This will not create a list of millions of elements at once.
Final Summary: Two Key Understandings Every Beginner Must Establish
1. Python is slow, not because of the language itself, but due to incorrect data structure choices and inefficient coding practices.
The complexity differences between lists, dictionaries, and sets account for 90% of performance bottlenecks.
2. Any occurrence of:
- • Large loops
- • Large data
- • Repeated calculations
- • A large number of objects
- • Using exceptions as flow controlare all sources of performance degradation.
Mastering the above 10 points will immediately elevate your Python speed to a new level.