Techniques to make your Python code faster

Python code can be slow - here's 3 ways to speed it up.

Python is a versatile language and has the ability to build out applications rapidly and at scale. However, one of the biggest drawbacks is its raw performance, especially for CPU-intensive tasks.

While it’s not noticeable in day-to-day applications, it can be noticed when doing extensive and large computations, or when handling data-intensive tasks that require concurrent processing.

Here, I want to provide 3 ways that you can increase the performance and speed of your python code.

Method 1: Asynchronous programming using asyncio

The asyncio package allows Python to manage other tasks while waiting for I/O operations to finish. Examples of this include database queries or network responses.

Think of it like grabbing a cup of coffee from your favorite coffee shop. You’ll order your coffee, then stand around and wait for the coffee to get done, then go grab a seat and start reading your book. This is known as synchronous: you’re waiting on one task to finish to do the next task.

Asynchronous is when you order your coffee and instead of waiting at the bar, you’ll go grab your seat and begin reading your book while your coffee is being made. You’ll do other tasks while another task is waiting to be completed.

Here’s how this can be simulated in code:

import asyncio

async def make_coffee():
    print("Making coffee...")
    # Simulating making coffee
    await asyncio.sleep(5)
    print("Coffee is ready!")

async def read_book():
    print("Reading a book...")
    # Simulate reading for a bit
    await asyncio.sleep(8) 
    print("Done reading!")

async def morning_routine():
    # Run both tasks simultaneously
    await asyncio.gather(make_coffee(), read_book()) 

asyncio.run(morning_routine())

Method 2: Utilizing Python’s built-ins

Python’s built-in functions such as map() and filter() and using list comprehensions and generators will help speed up your code.

Generators generate values on an as-needed basis. This saves memory and improving performance, especially for large datasets/data streams.

Similarly, implementing list comprehension reduces the overhead of multiple append operations (which can be expensive, at scale).

Here’s a small list of a few other built-ins you can leverage:

  • functools.lru_cache: A decorator that dramatically speed up functions by memorizing expensive function calls and storing the results in a cache to avoid repeated calculations.

  • itertools module: A collection of fast, memory-efficient tools that are useful for creating and manipulating iterators.

  • collections module: This module contains specialized container data structures and data types which are optimized for specific use cases.

Method 3: Identifying bottlenecks using profiling tools

Sometimes, it’s hard to spot where your code is running slow. Using profiling tools such as cProfile or line_profile will allow you to pinpoint where the bottlenecks in your code are at.

From here, you can then write more efficient data structures, caching results, etc.

Let’s say that you’re looking to do a comparison between 2 ways of finding the sum of squares. You can use cProfile to determine which one is more efficient:

import cProfile

def sum_of_squares_unoptimized(n):
    return sum(i * i for i in range(1, n + 1))

def sum_of_squares_optimized(n):
    return sum([i * i for i in range(1, n + 1)])

def main():
    n = 10000
    print("Running unoptimized sum of squares...")
    profiler_unoptimized = cProfile.Profile()
    profiler_unoptimized.enable()
    result_unoptimized = sum_of_squares_unoptimized(n)
    profiler_unoptimized.disable()
    profiler_unoptimized.print_stats(sort='time')

    print("\nRunning optimized sum of squares...")
    profiler_optimized = cProfile.Profile()
    profiler_optimized.enable()
    result_optimized = sum_of_squares_optimized(n)
    profiler_optimized.disable()
    profiler_optimized.print_stats(sort='time')

main()

Which results in the following tables being printed:

Table showing the performance of sum of squares using cProfile

📧Join the Python Snacks Newsletter!🐍

Want even more Python-related content that’s useful? Here’s 3 reasons why you should subscribe the Python Snacks newsletter:

  1. Get Ahead in Python with bite-sized Python tips and tricks delivered straight to your inbox, like the one above.

  2. Exclusive Subscriber Perks: Receive a curated selection of up to 6 high-impact Python resources, tips, and exclusive insights with each email.

  3. Get Smarter with Python in under 5 minutes. Your next Python breakthrough could just an email away.

You can unsubscribe at any time.

Interested in starting a newsletter or a blog?

Do you have a wealth of knowledge and insights to share with the world? Starting your own newsletter or blog is an excellent way to establish yourself as an authority in your field, connect with a like-minded community, and open up new opportunities.

If TikTok, Twitter, Facebook, or other social media platforms were to get banned, you’d lose all your followers. This is why you should start a newsletter: you own your audience.

This article may contain affiliate links. Affiliate links come at no cost to you and support the costs of this blog. Should you purchase a product/service from an affiliate link, it will come at no additional cost to you.

Reply

or to participate.