I've been writing Python for 5 years now. I've shipped production code, built trading bots, automated boring tasks. And I still fall for some of these traps. Regularly.
Here's the embarrassing truth about mistakes that even experienced devs make. With ChatGPT and Copilot helping us code, you'd think we'd be past these. Nope.
1. Using `except:` Without Specific Exceptions
We all know this is bad. We've all read the "don't use bare except" articles. And yet:
try:
do_something_risky()
except:
print("Something went wrong")
Still see this in production. Still catch myself doing it when I'm in a hurry.
The problem? This catches everything. KeyboardInterrupt. SystemExit. Even GeneratorExit. Your program won't shut down properly. Debugging becomes impossible because you have no idea what actually failed.
The fix is boring:
try:
do_something_risky()
except SpecificError as e:
print(f"Specific error: {e}")
But we're lazy. So we don't.
2. Mutable Default Arguments
This one is a classic. Every Python tutorial warns about it. And yet:
def append_item(item, my_list=[]):
my_list.append(item)
return my_list
print(append_item(1)) # [1]
print(append_item(2)) # [1, 2] # WTF?
The list is created once when the function is defined, not each time it's called. So it persists between calls.
I know this. You know this. We still forget. Usually at 2 AM when debugging a weird bug that "shouldn't be happening."
Fix:
def append_item(item, my_list=None):
if my_list is None:
my_list = []
my_list.append(item)
return my_list
3. Not Using `if __name__ == "__main__":`
For scripts, this matters. For modules, it matters even more.
I had a script that imported another module. That module had code at the top level that ran on import. It connected to a database. In production. When I just wanted a utility function.
Took me an hour to figure out why my innocent import was causing database connections.
Now I always use:
def main():
# actual code here
pass
if __name__ == "__main__":
main()
4. String Concatenation in Loops (I'm Guilty)
This is the one I still do. Every few months, I catch myself doing:
result = ""
for item in items:
result += str(item) + ", "
It's fine for small lists. But for large ones? O(n²) performance because strings are immutable. Each += creates a new string.
The right way:
result = ", ".join(str(item) for item in items)
Or for more complex building:
parts = []
for item in items:
parts.append(str(item))
result = ", ".join(parts)
I know this. I've benchmarked it. I still write the slow version first, then fix it later. Old habits.
5. Not Closing File Handles
We all know about `with` statements. We've seen the memory leak warnings. And yet:
f = open('data.txt', 'r')
data = f.read()
# forgot to f.close()
In a long-running process? That's a file descriptor leak. On Linux, you'll hit the limit eventually. Your program crashes with "Too many open files."
Just use the context manager:
with open('data.txt', 'r') as f:
data = f.read()
# Automatically closed, even if exceptions happen
6. Reinventing the Standard Library
I spent 2 hours once writing a function to parse CSV files. Handling quotes, commas in fields, edge cases.
Then I remembered: import csv. It's right there. Batteries included. Tested. Optimized.
Other things I've unnecessarily rewritten:
- HTTP requests (use
requests) - JSON parsing (use
json) - Date/time math (use
datetime) - Regular expressions for simple splits (use
str.split())
Check the standard library first. It's usually better than what you'll write.
7. Not Writing Tests Until It's Too Late
Every project starts with "I'll write tests later." Later never comes. Then you're debugging in production at 3 AM.
I've learned this lesson the hard way maybe 10 times. The trading bot that lost money because of a timezone bug? No tests. The web scraper that broke silently for a week? No tests. The API that returned wrong data for 200 users? No tests.
Now I force myself to write at least one test before I consider a feature "done." Even a bad test is better than no test. It catches the obvious stuff.
The Bottom Line
We're human. We make mistakes. The goal isn't perfection ā it's making fewer of the same mistakes over time.
I still catch myself making #4. Probably always will. But at least now I recognize it and fix it before it causes problems.
What's your recurring Python mistake? The one you know is wrong but still do sometimes?