DEV Community

Cover image for Speed Up Your Python Code with These Underrated Tricks
Ramkumar M N
Ramkumar M N

Posted on

16 4 6 5 7

Speed Up Your Python Code with These Underrated Tricks

Python is widely celebrated for its simplicity and readability, making it a favorite among beginners and seasoned developers alike. However, it's also known that Python may not always be the fastest language when it comes to raw execution speed, especially in compute-intensive scenarios.

Fortunately, with a thoughtful approach and the application of smart performance optimization techniques, you can significantly boost the speed and efficiency of your Python code without compromising its clean and elegant syntax.

From leveraging built-in functions and data structures to adopting libraries like NumPy, using just-in-time compilers like Numba, or optimizing loops and memory usage, there are many powerful strategies at your disposal. These performance enhancements allow you to write Python code that not only remains highly readable and maintainable but also delivers near-native speed in the right contexts.

1. Use List Comprehensions Instead of Loops

Loops are slower compared to list comprehensions, which are optimized in Python.

❌ Slow:

squared = []
for i in range(10):
    squared.append(i ** 2)
Enter fullscreen mode Exit fullscreen mode

✅ Faster:

squared = [i ** 2 for i in range(10)]
Enter fullscreen mode Exit fullscreen mode

2. Use join() Instead of String Concatenation

String concatenation in a loop is inefficient since strings are immutable.

❌ Slow:

result = ""
for word in ["Hello", "World"]:
    result += word + " "
Enter fullscreen mode Exit fullscreen mode

✅ Faster:

result = " ".join(["Hello", "World"])
Enter fullscreen mode Exit fullscreen mode

3. Use map() Instead of Loops for Transformations

Built-in functions like map() can be faster than explicit loops.

❌ Slow:

numbers = [1, 2, 3, 4]
squared = []
for num in numbers:
    squared.append(num ** 2)
Enter fullscreen mode Exit fullscreen mode

✅ Faster:

squared = list(map(lambda x: x ** 2, numbers))
Enter fullscreen mode Exit fullscreen mode

4. Use set for Faster Membership Checks

Checking for values in a list (O(n)) is slower than using a set (O(1)).

❌ Slow:

items = ["apple", "banana", "cherry"]
if "banana" in items:
    print("Found!")
Enter fullscreen mode Exit fullscreen mode

✅ Faster:

items = {"apple", "banana", "cherry"}  # Using a set
if "banana" in items:
    print("Found!")
Enter fullscreen mode Exit fullscreen mode

5. Use functools.lru_cache for Memoization

If a function performs expensive calculations, cache results to speed up repeated calls.

from functools import lru_cache

@lru_cache(maxsize=1000)
def expensive_function(n):
    print("Computing...")
    return n * n
Enter fullscreen mode Exit fullscreen mode

6. Use multiprocessing for Parallel Execution

Python’s multiprocessing module allows you to use multiple CPU cores.

from multiprocessing import Pool

def square(n):
    return n ** 2

with Pool(4) as p:
    results = p.map(square, range(10))
Enter fullscreen mode Exit fullscreen mode

7. Avoid Global Variables

Python slows down when accessing global variables in loops. Use local variables instead.

❌ Slow:

x = 10
def compute():
    for _ in range(1000000):
        global x
        x += 1
Enter fullscreen mode Exit fullscreen mode

✅ Faster:

def compute():
    x = 10  # Local scope is faster
    for _ in range(1000000):
        x += 1
Enter fullscreen mode Exit fullscreen mode

8. Use cython or numba for Heavy Computation

If your Python code involves a lot of number crunching, use cython or numba for just-in-time (JIT) compilation.

from numba import jit

@jit(nopython=True)
def fast_function(n):
    return n * n
Enter fullscreen mode Exit fullscreen mode

9. Use itertools for Memory-Efficient Iteration

Instead of storing large lists in memory, use iterators from itertools.

from itertools import islice

with open("large_file.txt") as f:
    first_10_lines = list(islice(f, 10))
Enter fullscreen mode Exit fullscreen mode

10. Prefer f-strings Over format()

Python’s f-strings are faster than .format() for string formatting.

❌ Slow:

name = "John"
greeting = "Hello, {}".format(name)
Enter fullscreen mode Exit fullscreen mode

✅ Faster:

name = "John"
greeting = f"Hello, {name}"
Enter fullscreen mode Exit fullscreen mode

11. Use enumerate() Instead of Range

Avoid manually managing an index when iterating through lists.

❌ Slow Approach:

items = ["apple", "banana", "cherry"]
for i in range(len(items)):
    print(i, items[i])
Enter fullscreen mode Exit fullscreen mode

✅ Faster Approach:

items = ["apple", "banana", "cherry"]
for i, item in enumerate(items):
    print(i, item)
Enter fullscreen mode Exit fullscreen mode

Why? enumerate() is optimized and makes the code more readable.


12. Use zip() for Parallel Iteration

Instead of iterating through multiple lists using indexing, use zip().

❌ Slow Approach:

names = ["Alice", "Bob", "Charlie"]
ages = [25, 30, 35]

for i in range(len(names)):
    print(names[i], ages[i])
Enter fullscreen mode Exit fullscreen mode

✅ Faster Approach:

for name, age in zip(names, ages):
    print(name, age)
Enter fullscreen mode Exit fullscreen mode

Why? zip() is faster and avoids manual indexing.


13. Use itertools for Memory Efficiency

For large datasets, itertools can help avoid unnecessary memory usage.

✅ Example: Using islice() to Limit Iteration

from itertools import islice

big_list = range(10**6)
first_ten = list(islice(big_list, 10))
print(first_ten)
Enter fullscreen mode Exit fullscreen mode

Why? This avoids creating a full list in memory.


14. Use defaultdict for Cleaner Dictionary Operations

Avoid key existence checks by using defaultdict.

❌ Slow Approach:

counts = {}
words = ["apple", "banana", "apple"]

for word in words:
    if word in counts:
        counts[word] += 1
    else:
        counts[word] = 1
Enter fullscreen mode Exit fullscreen mode

✅ Faster Approach:

from collections import defaultdict

counts = defaultdict(int)
for word in words:
    counts[word] += 1
Enter fullscreen mode Exit fullscreen mode

Why? defaultdict initializes values automatically, reducing checks.


15. Use Counter for Fast Frequency Counts

Counting elements in a list is easier with collections.Counter().

❌ Slow Approach:

words = ["apple", "banana", "apple"]
word_counts = {}

for word in words:
    if word in word_counts:
        word_counts[word] += 1
    else:
        word_counts[word] = 1
Enter fullscreen mode Exit fullscreen mode

✅ Faster Approach:

from collections import Counter

words = ["apple", "banana", "apple"]
word_counts = Counter(words)
Enter fullscreen mode Exit fullscreen mode

Why? Counter is optimized for frequency calculations.


16. Use deque Instead of Lists for Fast Insertions

Lists are slow when inserting at the beginning. Use deque instead.

❌ Slow Approach:

items = []
items.insert(0, "new")
Enter fullscreen mode Exit fullscreen mode

✅ Faster Approach:

from collections import deque

items = deque()
items.appendleft("new")
Enter fullscreen mode Exit fullscreen mode

Why? deque has O(1) insertions, while lists have O(n).


17. Use any() and all() Instead of Loops

Instead of manually checking conditions, use any() or all().

❌ Slow Approach:

values = [0, 0, 1, 0]

found = False
for v in values:
    if v == 1:
        found = True
        break
Enter fullscreen mode Exit fullscreen mode

✅ Faster Approach:

values = [0, 0, 1, 0]
found = any(v == 1 for v in values)
Enter fullscreen mode Exit fullscreen mode

Why? any() short-circuits as soon as it finds a True value.


18. Use sorted() with key for Custom Sorting

Instead of sorting manually, use the key argument.

❌ Slow Approach:

people = [("Alice", 25), ("Bob", 30), ("Charlie", 20)]
people.sort(key=lambda x: x[1])
Enter fullscreen mode Exit fullscreen mode

✅ Faster Approach:

people = [("Alice", 25), ("Bob", 30), ("Charlie", 20)]
sorted_people = sorted(people, key=lambda x: x[1])
Enter fullscreen mode Exit fullscreen mode

Why? sorted() is more optimized than manual sorting.


19. Use @staticmethod and @classmethod to Avoid Unnecessary Instantiations

Instead of creating unnecessary objects, use @staticmethod and @classmethod.

❌ Slow Approach:

class Math:
    def square(self, x):
        return x * x

m = Math()
print(m.square(5))
Enter fullscreen mode Exit fullscreen mode

✅ Faster Approach:

class Math:
    @staticmethod
    def square(x):
        return x * x

print(Math.square(5))
Enter fullscreen mode Exit fullscreen mode

Why? No need to instantiate the class to use the method.


20. Use dataclass Instead of Regular Classes

Python's dataclass reduces boilerplate and improves performance.

❌ Slow Approach:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
Enter fullscreen mode Exit fullscreen mode

✅ Faster Approach:

from dataclasses import dataclass

@dataclass
class Person:
    name: str
    age: int
Enter fullscreen mode Exit fullscreen mode

Why? dataclass automatically generates __init__, __repr__, and __eq__.


Final Thoughts

By combining these 20 Python tricks, you can significantly improve performance, reduce memory usage, and write cleaner code.

Did I miss any of your favorite Python optimizations? Let me know in the comments!


📢 Let’s Connect!

💼 LinkedIn | 📂 GitHub | ✍️ Dev.to | 🌐 Hashnode


💡 Join the Conversation:

  • Found this useful? Like 👍, comment 💬.
  • Share 🔄 to help others on their journey!
  • Have ideas? Share them below!
  • Bookmark 📌 this content for easy access later.

Let’s collaborate and create something amazing! 🚀


Heroku

Deploy with ease. Manage efficiently. Scale faster.

Leave the infrastructure headaches to us, while you focus on pushing boundaries, realizing your vision, and making a lasting impression on your users.

Get Started

Top comments (18)

Collapse
 
anmolbaranwal profile image
Anmol Baranwal

Write on medium too!

Collapse
 
ramkumar-m-n profile image
Ramkumar M N

Thanks Anmol Baranwal Subscriber!
I’ve been considering it. I would love to start writing on Medium too soon. Appreciate the encouragement

Regards,
Ram

Collapse
 
xwero profile image
david duymelinck • Edited

Instead of a list of options I would preferred to see more explanation why to use one or the other and also more context.

A list comprehension is faster than the map function when not using a lambda. So if you want speed the map example should be

def square(x):
    return x ** 2

numbers = [1, 2, 3, 4, 5]

squared = list(map(square, numbers))
Enter fullscreen mode Exit fullscreen mode

For 4 and 16, the fix is use the right data structure.

Avoid global variables is not only a speed fix, it makes the code more maintainable.

9 and 13 are the same thing, so there are 19 different things.

Between defaultdict and Counter, the latter will be faster as the first is more generic.

Only use @staticmethod and @classmethod when it is appropriate. Don't use them just to avoid class instantiation.

Collapse
 
ramkumar-m-n profile image
Ramkumar M N

Hi david duymelinck

Thanks so much for the thoughtful feedback, You're absolutely right. I appreciate you pointing out where more explanation would add value. Also great catch on the overlap between points 9 and 13. l will consolidate that. Feedback like yours really helps improve the quality.

Thanks & Regards,
Ram

Collapse
 
davinceleecode profile image
davinceleecode

Thanks for this article! 🔥

Collapse
 
ramkumar-m-n profile image
Ramkumar M N

Hi davinceleecode
Thanks for reading. Glad you liked it!

Regards,
Ram

Collapse
 
ion_finisher profile image
Teddy ASSIH

Great article. I would have appreciated a little more explanation (such as the data structures and algorithms behind certain python methods) on why you use a specific syntax rather than another.

Collapse
 
ramkumar-m-n profile image
Ramkumar M N

Hi Teddy Assih,
Thank you for the feedback. I will update the post with explanations.

Regards,
Ram

Collapse
 
nevodavid profile image
Nevo David

been using python for a bit and some of these little changes really do help, keeps me going back to refactor old code all the time

Collapse
 
ramkumar-m-n profile image
Ramkumar M N

Thank you for the acknowledgment Nevo David 😊👍

Collapse
 
anitaolsen profile image
Anita Olsen

Thank you so much for this! ✨

Collapse
 
ramkumar-m-n profile image
Ramkumar M N

Hi Anita Olsen,
You’re very welcome! I’m glad you found it helpful. 😊👍

Regards,
Ram

Collapse
 
nathan_tarbert profile image
Nathan Tarbert

pretty cool rundown tbh, i always forget about stuff like lru_cache till i see it used - you ever feel like most speed problems only show up when something actually breaks?

Collapse
 
ramkumar-m-n profile image
Ramkumar M N

Hi Nathan Tarbert,
Thanks for the comment. I’ve never really faced or noticed that myself. Most of the time, things just run fine unless I’m specifically benchmarking or digging into optimizations.

Regards,
Ram

Collapse
 
nathan_tarbert profile image
Nathan Tarbert

absolutely love little speed tricks like these honestly makes coding way less annoying over time

Collapse
 
ramkumar-m-n profile image
Ramkumar M N

Hi Nathan Tarbert

Good to hear that. Refactoring old code with small optimizations can really make a big difference over time. I'm glad the post sparked that motivation!

Thanks & Regards,
Ram

Collapse
 
rankmyai profile image
RankmyAI

Good!

Collapse
 
ramkumar-m-n profile image
Ramkumar M N

Thank you RankmyAl

DevCycle image

Fast, Flexible Releases with OpenFeature Built-in

Ship faster on the first feature management platform with OpenFeature built-in to all of our open source SDKs.

Start shipping