Analyze Fast. Analyze Quickly
Time is limited. Data analysis shouldn't take forever. What if you could analyze data faster without compromising on quality?
Meet Julius, the AI analyst that makes data analysis fast, intuitive, and code-free.
Built for anyone who works with data, from marketers and finance professionals to tech and business teams.
If you can type in English, you can analyze data with Julius.
Ask questions in plain English. Get insights in minutes, not hours
Clean, explore, and visualize data. No setup or code needed
Tackle real-world tasks without spreadsheets or complex formulas
Build reusable workflows in notebooks for repetitive work
Present insights and make smart, data-backed decisions
Try Julius for FREE and extract insights from data like a pro.
Introduction
Did you know that you can actually climb onto the roof of a building using a rope? You just have to tie it to a strong object. Now, just because you can do it does not mean you should. Using a rope puts you at risk. Fortunately, people have invented an easier and safer tool for getting to the roof of a building. It is called a ladder. A smart person would use it instead of a rope.
Guess what? The same idea applies to writing Python code. Just because you can write certain code from scratch does not mean that you should. If there is a library that someone else has already created, the smart choice is to use that library.
In this article, I want to talk about the libraries that others have written but that you are probably misusing, underusing, or not using at all. Yes, those libraries that you suck at using. Instead of reinventing the wheel, you should use these tools. By doing so, your code becomes cleaner, faster, and easier to understand.
1. itertools: You’re Writing Loops Like It’s 1999
I remember the feeling when I first discovered loops. Well, it wasn’t 1999, but I can tell you that it was a game changer. Loops are incredibly powerful. Instead of writing the same lines of code over and over for each item, you write it once, and the loop handles the repetition. This saves an immense amount of time and reduces the chance of errors. So, I don’t blame you for writing them like it’s 1999.
However, while loops are powerful, they do have limitations, especially in modern programming styles and for certain types of tasks. Understanding these limitations helps you choose the right tool for the job. Each iteration in a Python loop involves interpreter overhead, such as checking types and managing memory. This can add up significantly when working with large datasets.
To overcome this limitation, Python offers a handy built-in library called itertools. For example, let’s say you want to generate all unique pairs from a given list. The order doesn’t matter, and no item should be paired with itself. Here’s how you might write the code using traditional loops:
In this code, we are using nested for loops and a conditional check to avoid duplicate pairs and self-pairing. You can see that it works just fine. However, we cannot overlook the verbosity of the code. It requires nested loops and careful indexing (j = i + 1) to ensure uniqueness and avoid self-pairing. This manual indexing can lead to bugs. This makes the code less Pythonic.
To avoid this verbosity and reduce the risk of bugs, we can use the itertools library. The itertools.combinations() function directly generates all unique combinations of elements from iterable without repetition and without regard to order. Here is how you can rewrite the code using combinations from itertools:
Look at that! The code is much shorter, and its intent (combinations) is immediately clear, making it easier to read and understand. Now, that's Pythonic!
2. functools: You're Writing Expensive Code
You know, everything has a cost. That includes code. When code is executed, it uses resources. This means that code that is repeated is more expensive than code that is executed once. It’s a waste of resources to repeat something that could be done just once.
Let’s say we have a list that contains the same number repeated multiple times. We want to multiply each number by itself and return a list of the results.
Here’s how you might write expensive code:
This code is expensive because it repeats three times to match the number of items in the list. Basically, the slow_func is called three times with the argument 2. So, the process repeats, but the argument is the same. This is quite expensive.
Instead of using the expensive approach, you can use lru_cache from the functools library. Here is how you can rewrite this code using functools.lru_cache:
Look at that! Have you noticed the difference? The @lru_cache(maxsize=128) decorator transforms the fast_func function. When the function is first called, lru_cache allows it to execute normally. The decorator stores the result 4 in its cache, associated with the argument 2. On the second call, before executing fast_func, lru_cache checks if the result for the argument 2 is already in its cache. It finds 4 in the cache, so the function itself is not executed. It returns the 4 in the cache. This process is repeated for the third call of the function.
Basically, lru_cache prevents that expensive operation from running repeatedly for the same inputs. It's a form of memoization. That, ladies and gentlemen, is Pythonic.
3. typing: You're Just Winging It
If you ever organize an event and you do not tell people the expected dress code, don't be surprised if some people show up in flip-flops and shorts. Don't be mad either. How were they supposed to know?
That also applies to writing code. If you do not tell people the expected data types of your code, don't be mad if they try to pass strings instead of floats as arguments to your function. How are they supposed to know? To avoid this confusion, you can use type hints or type annotations in your code. Type hints indicate the expected data types for variables, function arguments, and function return values. They are hints because, by default, the Python interpreter does not enforce them at runtime; Python remains a dynamically typed language. Instead, they are primarily used by static type checkers and IDEs to analyze your code without running it. Here is an example of code without type hints:
Without hints, there's no information about what scores should look like. No type validation or help from the editor. You could pass in anything, and Python won't complain… until it breaks at runtime.
To improve the code, you can use the type library. Here is an improved version of the code using type hints:
We have now created an explicit structure for the function. The function now knows what a "score" item looks like. Many static checkers and IDEs will flag wrong-shaped data before you run the code.
Using type hints is especially important in larger codebases or when working in teams. It significantly reduces code ambiguity and makes code easy to debug and maintain.
4. collections: You’re Still Using Lists for Everything, Aren’t You?
Normal Python lists and dictionaries are extremely useful, and we should not underestimate their importance. However, trying to solve every problem with these data structures can result in unnecessarily long and repetitive code.
Let’s use a simple example to illustrate this. Suppose you want to find the frequency of elements in a string. Here is how you might do it, well, in a not-so-Pythonic way:
This works just fine. But look at how long the code is. This can be avoided if you know about the collections module. Python's collections module gives you data structures with intent. It makes your code not just shorter, but smarter. Let's rewrite the code using Counter from the collections module:
Boom! The code is now more Pythonic. The collections module is such a powerful library, and I advise you to explore it further.
5. datetime: You’re Treating Dates Like Strings (Please Stop)
Dealing with dates is one of the worst nightmares for people that are just learning Python. Let's say you have two dates and you are trying to find the difference between them. You may try something like this:
As you can see, this does not work. Even though we are seeing dates, Python is only seeing strings. This code does not work because you are attempting to subtract strings directly, which is not a defined operation in Python. Python strings do not have arithmetic operations like subtraction.
To calculate the difference between two dates, you need to convert the date strings into actual date objects first. Python's datetime module is specifically designed for this purpose. Using datetime.strptime() you can convert the strings into datetime objects. You'll need to provide the format string that matches your date string (e.g., "%Y-%m-%d" for "YYYY-MM-DD"). Here is the code:
Now it works. The result will be a timedelta object, which represents a duration.
Wrap-Up
Let’s end it here. We’re just scratching the surface when it comes to Python libraries. Here’s a good rule of thumb: when you’re writing code and it starts to feel clumsy or repetitive, take a moment to check if there’s already a library that can do the job for you.
So stop winging it. Write smarter Python. And maybe stop sucking at using these libraries.
If you found this helpful, share it with a fellow developer. Got a favorite Python library people always misuse? Drop it in the comments. I’d love to hear about it. And if you want more practical Python tips like this, make sure to subscribe to the newsletter. Thanks for reading.
Nice
Type hints are awesome