9 Python Performance Optimization Techniques for Faster Code

  1. Faster Loops: Prioritize Local Variables

In Python, accessing local variables is faster than accessing global variables or object attributes.


import timeit

class DataProcessor:
    def __init__(self):
        self.counter = 0

def test_attribute_access():
    processor = DataProcessor()
    for _ in range(1000):
        processor.counter += 1
    return processor.counter

def test_local_variable():
    processor = DataProcessor()
    local_counter = processor.counter
    for _ in range(1000):
        local_counter += 1
    processor.counter = local_counter
    return processor.counter

print(timeit.timeit(test_attribute_access, number=1000))
# 0.03894249998731308
print(timeit.timeit(test_local_variable, number=1000))
# 0.02581704199687195

This performance difference exists because when a function is compiled, its local variables are known, while external variables require lookup time. While this may seem minor, it can significantly impact performence when processing large datasets.

  1. Faster Execution: Leverage Built-in Modules and Libraries

Most Python's built-in modules and libraries are implemented in C, which is a faster, lower-level language. We should utilize these built-in solutions rather than reinventing the wheel.


import timeit
import random
from math import sqrt

def custom_prime_check(numbers):
    primes = []
    for num in numbers:
        if num > 1:
            is_prime = True
            for i in range(2, int(sqrt(num)) + 1):
                if num % i == 0:
                    is_prime = False
                    break
            if is_prime:
                primes.append(num)
    return primes

def builtin_prime_check(numbers):
    return [n for n in numbers if n > 1 and all(n % i != 0 for i in range(2, int(sqrt(n)) + 1))]

number_list = [random.randint(1, 1000) for _ in range(500)]

print(timeit.timeit(lambda: custom_prime_check(number_list), number=100))
# 0.1864542919835752
print(timeit.timeit(lambda: builtin_prime_check(number_list), number=100))
# 0.14171604199692535

The above example compares two approaches for identifying prime numbers in a list. The built-in approach using list comprehensions and the all() function is both faster and more concise than the custom implementation with explicit loops.

  1. Faster Function Calls: Implement Memoization with Cache Decorators

Caching is a technique to avoid redundant computations and speed up program execution. Python provides a ready-to-use decorator for this purpose - @functools.lru\_cache.


import timeit
import functools

def recursive_factorial(n):
    if n <= 1:
        return 1
    return n * recursive_factorial(n - 1)

@functools.lru_cache(maxsize=None)
def cached_factorial(n):
    if n <= 1:
        return 1
    return n * cached_factorial(n - 1)

print(timeit.timeit(lambda: recursive_factorial(25), number=100))
# 0.8725418750361915
print(timeit.timeit(lambda: cached_factorial(25), number=100))
# 0.0008958750038194656

The results demonstrate how the @functools.lru\_cache decorator significantly improves performance. The basic factorial function is inefficient because it repeatedly calculates the same values. The cached version is much faster as it stores previously computed results and retrieves them when needed.

  1. Faster Infinite Loops: Prefer "while 1" Over "while True"

When creating infinite loops, both while True and while 1 work, but there's a slight performance difference between them.


import timeit

def true_based_loop():
    count = 0
    while True:
        if count >= 1000:
            break
        count += 1

def one_based_loop():
    count = 0
    while 1:
        if count >= 1000:
            break
        count += 1

print(timeit.timeit(true_based_loop, number=10000))
# 0.1719878750129753
print(timeit.timeit(one_based_loop, number=10000))
# 0.1624124999956058

As shown, while 1 is marginally faster because 1 is a literal constant, while True is a global name that requires lookup. However, modern Python interpreters have optimized this difference to be negligible. Moreover, while True is more readable and should be preferred unless you're in a performance-critical section of code.

  1. Faster Startup: Strategic Module Imports

While it's common practice to import all modules at the top of a Python script, this isn't always necessary. For large modules, importing them only when needed can improve startup performance.


def process_large_dataset():
    import pandas as pd
    # Function continues with pandas operations
    
def main():
    # Some operations that don't require pandas
    print("Starting application...")
    
    # Only when pandas is needed, it gets imported
    if need_pandas():
        process_large_dataset()

The above code demonstrates "lazy loading" - the pandas module is only imported when process\_large\_dataset() is called. This approach saves resources and reduces script startup time if the function is never called during execution. This technique is particularly useful for applications with optional features or plugins.

Tags: python performance-optimization coding-tips memoization CPython

Posted on Fri, 11 Sep 2026 16:31:21 +0000 by Rippie