I remember when we used to count bytes. Now, people import a library just to print a string. It’s disgusting. But let’s talk about why I’m wrong.
It was 4:12 AM. I was staring at a terminal window, my eyes burning from the blue light and the third pot of coffee that tasted more like battery acid than beans. I was hunting a memory leak in a legacy C++ telemetry module—code I’d written in 1998 and hadn’t touched since the Bush administration. Somewhere, in a nested loop of pointers to pointers, a delete had gone missing. Or maybe it was a double-free. Or maybe the heap was just tired of my nonsense.
In the 80s, if you leaked memory, the machine stopped. You felt the consequence. You learned. Modern developers? They just throw more RAM at the problem. They treat memory like an infinite resource, like air. It’s offensive. I spent three decades hand-optimizing assembly for 8-bit microcontrollers where 64KB was a luxury suite. And here I was, thirty years later, losing sleep over a pointer that refused to die.
Then came the “Automation Initiative.” Management decided we needed to “move faster.” They handed me a project: automate the hardware-in-the-loop testing for the new sensor array. “Use Python,” they said. “It’s easy,” they said. I scoffed. I told them I didn’t write “scripting languages.” I told them I didn’t want to deal with a language that used whitespace for logic. It felt like writing code in a coloring book.
But I was tired. The C++ leak was still there, mocking me. So, I opened a terminal. I typed python3. And I started to see the horror.
Table of Contents
The Garbage Collector is My New Best Friend and I Hate It
The first thing that hits you is the sheer, unadulterated bloat. In C, an integer is four bytes. Maybe eight if you’re feeling fancy. In Python? It’s a whole production. Every single thing in Python is an object. A “simple” integer carries around a backpack of metadata—reference counts, type pointers, size information. It’s like bringing a suitcase to a day trip.
Look at this. I ran this in the REPL just to see how much damage we were doing to the silicon.
Python 3.12.1 (main, Dec 8 2023, 15:31:13) [GCC 12.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> x = 1
>>> sys.getsizeof(x)
28
>>> y = 10**100
>>> sys.getsizeof(y)
72
>>> # Twenty-eight bytes for the number one. Disgusting.
>>> # In my day, we fit entire programs in 28 bytes.
Twenty-eight bytes for the number one. I could have controlled a nuclear reactor with twenty-eight bytes in 1984. But here’s the thing—the “Reluctant Confession” part. While I was busy counting those 28 bytes, I realized I hadn’t thought about malloc() once. I hadn’t thought about free(). I hadn’t worried about buffer overflows or dangling pointers.
The garbage collector—a concept I used to view as a crutch for the weak-minded—just handled it. It uses reference counting as its primary mechanism, supplemented by a cyclic garbage collector to catch those nasty self-referencing loops that would otherwise haunt your heap forever. To understand what is actually happening under the hood, you have to look at the bytecode, but for the first time in my life, I didn’t have to. I just wrote the logic. The machine did the chores. I felt like a traitor.
Guido’s Revenge and the Great Unicode War
I spent a lot of time researching the history of this thing while waiting for my C++ builds to finish. Guido van Rossum started this as a “hobby” project in December 1989. He wanted something between C and the Shell. He named it after Monty Python, which explains why the documentation is full of jokes that aren’t funny when you’re trying to debug a race condition at midnight.
Then there was the Python 2 to Python 3 transition. I still have scars from that. It was a decade-long civil war. The main issue? Strings. In Python 2, strings were just bytes. It was honest. It was simple. In Python 3, everything became Unicode. Suddenly, your code would blow up because someone sent a character that didn’t exist in 1985.
But I have to admit, looking back, they were right. The world isn’t just ASCII anymore. If you want to build something that works globally, you can’t just pretend everything is a 7-bit character. Python 3 forced us to care about the difference between bytes and str. It was painful, like pulling a tooth, but the mouth is healthier now.
Namespaces are a Honking Great Idea (And I Hate That I Agree)
In C, everything is a fight. You want to use a function from another file? Better hope you didn’t have a naming collision. Better hope your header guards are solid. Better hope you didn’t include the same file twice in a way that makes the compiler throw a tantrum.
Python handles this with namespaces. Everything is a dictionary. Literally. You want to see the variables in your current scope? Just call locals(). You want to see the global ones? globals(). It’s all just hash maps.
>>> # Let's see the overhead of a simple dictionary.
>>> my_config = {'port': 8080, 'timeout': 30}
>>> sys.getsizeof(my_config)
232
>>> # Two hundred and thirty-two bytes for two integers and two strings.
>>> # I could have stored the entire library of congress in that much space in 1982.
>>> # But look at how I access it:
>>> my_config['port']
8080
The “Zen of Python” (which is actually PEP 20, for those who care about the rules) says “Namespaces are one honking great idea—let’s do more of those!” And they did. Every module is its own namespace. No more prefixing every function with MY_PROJECT_SUBSYSTEM_FUNCTION_NAME. You just import subsystem and call subsystem.function(). It’s clean. It’s organized. It makes me feel like I’m wearing a suit instead of my grease-stained “I Heart Assembly” t-shirt.
Structural Pattern Matching for People Who Miss Switch Statements
For years, Python didn’t have a switch statement. You had to use if-elif-else chains like a caveman. It was one of my primary complaints. “How can you call this a real language if I can’t jump-table my way through a set of constants?” I’d ask.
Then came PEP 634. Structural Pattern Matching. It arrived in Python 3.10, and it’s better than any switch statement I’ve ever used in C. It doesn’t just match values; it matches shapes.
>>> def handle_command(cmd):
... match cmd.split():
... case ["QUIT"]:
... return "Shutting down..."
... case ["LOAD", filename]:
... return f"Loading {filename}"
... case ["MOVE", x, y] if int(x) > 0:
... return f"Moving to {x}, {y}"
... case _:
... return "Unknown command"
...
>>> handle_command("LOAD firmware.bin")
'Loading firmware.bin'
>>> handle_command("MOVE 10 20")
'Moving to 10, 20'
This is where I started to crack. Writing that in C would involve strtok, a bunch of strcmp calls, and probably three different buffer overflow vulnerabilities. In Python, it’s five lines of readable code. People keep asking me what is the point of this “executable pseudocode,” and I’m starting to realize the point is that I can go home at 5:00 PM instead of 4:00 AM.
CPython is Just a C Program with a God Complex
To truly understand Python, you have to realize it’s a lie. Python isn’t “running” on your CPU. CPython (the standard implementation) is a C program that reads your script, compiles it into bytecode, and then runs that bytecode on a virtual stack machine.
When you run a Python script, you’re just feeding data to a massive C loop called _PyEval_EvalFrameDefault. It’s a giant switch statement (ironic, right?) that handles every opcode. This is why it’s “slow.” Every instruction has the overhead of the interpreter loop.
But here’s the secret: most of the heavy lifting isn’t done in Python. When you use numpy or pandas or even the built-in sort(), you’re calling into highly optimized C or Fortran code. Python is just the “glue.” It’s the manager who doesn’t know how to use a wrench but is really good at telling the mechanics what to do.
The GIL (Global Interpreter Lock) is the boogeyman of the Python world. It prevents multiple native threads from executing Python bytecodes at once. It’s the reason Python “can’t do true concurrency.” In my world, we handle concurrency with interrupts and DMA. In Python, you use multiprocessing to spawn entirely new interpreter instances because the GIL won’t let you share the sandbox. It’s inefficient. It’s clunky. And yet… for 99% of automation tasks, it doesn’t matter. The bottleneck is the network or the disk, not the GIL.
PEP 8: The Nanny State of Formatting
I hate being told how to format my code. I like my braces where I like them. I like my tabs. I like my idiosyncratic spacing that only I understand.
Then comes PEP 8. The “Style Guide for Python Code.” It tells you how many spaces to use (four, never tabs), where to put your imports, and how long your lines should be. At first, I felt insulted. Who is Guido to tell me how to indent?
But then I had to read code written by a junior dev who grew up on JavaScript. If that code had been in C, it would have been a nightmare of inconsistent bracing and “clever” one-liners. But because of PEP 8 and tools like flake8 or black, the code looked… exactly like mine. I could read it instantly. No mental translation layer required.
It turns out that when everyone agrees to follow the same boring rules, the code becomes transparent. You stop looking at the syntax and start looking at the logic. It’s a bitter pill to swallow for an old dog who likes his “clever” tricks, but it’s the truth.
The Glue That Keeps My Sanity from Fragmenting
I’ve spent the last month rewriting my hardware test suite in Python. What took me three months in C++ took me three weeks in Python. I used pyserial to talk to the UART, requests to log results to the internal server, and pytest to manage the test cases.
I didn’t have to write a single linked list. I didn’t have to debug a memory corruption issue caused by a string that wasn’t null-terminated. I didn’t have to fight the linker.
Is it “bloated”? Yes. Does it use too much RAM? Absolutely. Is it “real” programming? My ego says no, but my paycheck and my sleep schedule say yes.
Python is the “glue” language. It’s not meant to replace the firmware I write for the ARM Cortex-M4. It’s meant to surround it. It’s the scaffolding that lets me build the real stuff faster. It’s the realization that not every problem is a nail, and sometimes you don’t need a 20lb sledgehammer when a simple screwdriver will do.
I still miss the 80s. I miss knowing exactly where every bit is stored in the registers. I miss the simplicity of a system that only does exactly what you tell it to do. But I don’t miss the 4:00 AM memory leaks.
If you’re an embedded guy like me, don’t fight it. Don’t be the guy still using a slide rule when everyone else has a calculator. Python isn’t going to take your job; it’s going to make your job suck less. Just don’t ask me to like the 28-byte integers. That’s a bridge too far.
I’m still going to write my drivers in C. I’m still going to optimize my hot loops in Assembly. But for everything else? For the automation, the data crunching, the “get it done by Friday” tasks? I’ll be using the “executable pseudocode.”
Just don’t tell the kids I said that. They’re already soft enough. They think “low level” means writing a C# wrapper. They wouldn’t know a stack overflow if it hit them in the face. But at least with Python, they can’t break my build as easily. And that, in itself, is worth the 28 bytes.
Now, if you’ll excuse me, I have a C-extension to write. Because as much as I’ve grown to tolerate Python, there are some things that just need the cold, hard efficiency of a pointer. And I’m the only one left who knows how to use them without bleeding all over the heap.
The transition from Python 2 to 3 was a mess, the GIL is a relic of a simpler time, and the memory overhead is a crime against engineering. But it works. And in this industry, “it works” is the only metric that matters when the sun starts coming up and you’re still at your desk.
I’m a convert. A grumpy, cynical, reluctant convert. Now get off my lawn, I have some “wheels” to install. Whatever the hell those are.
Post-Script for the “Modern” Devs:
If you ever use the word “Empower” in a pull request, I will personally find your desk and replace your mechanical keyboard with a membrane one from 1994. You’ve been warned. Write your code, keep it lean, and for the love of Guido, read PEP 8 before you check in that mess. I’m watching. I’m always watching. And I still have my copy of K&R within arm’s reach. Don’t test me.
Related Articles
Explore more insights and best practices: