A mental model for understanding Python’s role
Anyone who started programming in the early 1980s might have started with Apple II BASIC, BBC BASIC or Sinclair BASIC (ZX-81 or ZX Spectrum) - and 6502 or Z80 assembler.
Those early environments were all defined by immediacy. You typed something in; the machine did something. There was no ambiguity about what the language was for.
Since then a professional programmer might have ventured through FORTRAN, C, C++, UNIX shell, Visual Basic, VBA, VB.NET, C#, F#, JavaScript and more recently Rust, Zig, Nim and Odin. Every one of those fits elegantly into a mental slot: Systems language, Application language, Functional language, Runtime language or Tooling language.
Python, oddly, doesn’t. It can be tricky, for an expe…
A mental model for understanding Python’s role
Anyone who started programming in the early 1980s might have started with Apple II BASIC, BBC BASIC or Sinclair BASIC (ZX-81 or ZX Spectrum) - and 6502 or Z80 assembler.
Those early environments were all defined by immediacy. You typed something in; the machine did something. There was no ambiguity about what the language was for.
Since then a professional programmer might have ventured through FORTRAN, C, C++, UNIX shell, Visual Basic, VBA, VB.NET, C#, F#, JavaScript and more recently Rust, Zig, Nim and Odin. Every one of those fits elegantly into a mental slot: Systems language, Application language, Functional language, Runtime language or Tooling language.
Python, oddly, doesn’t. It can be tricky, for an experienced programmer, to grasp what it was and how it related to the other languages or which slot to put it in. Often, they will conclude "I don’t like Python" and express confusion at its vast popularity - and even primacy - in the 2020s.
A slippery language
Traditionally we’re taught to classify languages along a few axes:
- compiled vs interpreted
- scripting vs “real” languages
- imperative vs OO vs functional
Python fits poorly into all of them.
It isn’t a compiled language in the C or Rust sense: it doesn’t result in a standalone executable. But it isn’t purely interpreted either, since it must be processed before execution. It supports imperative, object-oriented and functional styles but isn’t optimized for any of them. It began as a scripting language, but today it’s used to build large, long-running systems.
So just what IS Python?
Python is not a binary-producing language
The turning point is to realize that Python is not defined by the artefact it produces.
C, C++, Rust, Zig and Fortran produce binaries that can be directly run. The output is the thing. Once compiled, the language more or less disappears.
Python doesn’t work like that.
Python source code is compiled to bytecode, and that bytecode is executed by a virtual machine. The VM, the object model, the garbage collector and the standard library are not incidental. They are Python. A Python program needs this ecosystem to run; it can’t run standalone, unless they are all bundled in with it.
In structural terms, Python sits alongside languages with runtimes and ecosystems:
- .NET (C#, F#, VB.NET)
- the JVM (Java, Scala, Kotlin, Clojure)
In all three cases, the runtime is the unit of execution, not the compiled artefact.
“Interpreted vs compiled” is therefore a false dichotomy. CPython parses Python source-code to an Abstract Syntax Tree (AST), compiles it to bytecode and then executes that bytecode on a VM. That’s not conceptually different from Java or .NET — just simpler and often slower, while the runtime startup overhead persists.
Python’s real role: orchestration
The solution to the puzzle of "What IS Python?" is to realize that Python is a runtime-centric language, whereupon its real role becomes obvious.
Python is not primarily about doing work. It’s about controlling work. It’s exceptional good and for quickly lashing stuff together: like Lego, Meccano or snap-on tooling than traditional software construction.
The most important Python libraries — NumPy, SciPy, Pandas, PyTorch, TensorFlow — are not written in Python in any meaningful sense. Python provides the API, the glue and the control flow. The heavy lifting happens in underlying libraries written in C, C++, Fortran or CUDA - anything that can expose a C ABI (Application Binary Interface).
Python performs the same role over its libraries as:
- SQL over databases
- shell over Unix
- VBA over Office
It is an orchestration language sitting above high-performance systems. That’s why it thrives in scientific computing, data pipelines and machine learning. It lets you build rapidly and easily, with simply syntax, whilst the underlying libraries deliver the performance. So long as orchestration overhead is low, Pythobn-based systems can scale surprisingly far.
Why Python still feels slippery
Even with this framing, Python can still feel oddly unsatisfying if you come from strongly structured languages.
Compared with .NET or the JVM, Python has:
- weak static guarantees
- loose module boundaries
- a simpler, leakier object model
If you’re used to the discipline of C#, F# or Rust, Python can feel vague. Things work — until they don’t — and the language often declines to help you reason about correctness ahead of time.
It turns out that being able to throw things together quickly, in easy-to-understand code, and ecosystem breadth are far more important for mass adoption than type-safety, compilation, raw-performance or architectural rigidity. Make something easy, and more people will do it, more often.
Python’s winning formula is to lower the barrier-to-entry for proof-of-concept and prototype stage projects - much like Visual BASIC and VBA did in the 1990s - and can even get to MVP (Minimum Viable Product). You can always make it faster, later, by translating critical paths into a compiled language.
Getting something working, at all and quickly, turns out to be hugely more important than getting it working fast or elegantly - something shell scripting showed us as far back as the 1970s.
Clearing up potential misunderstandings
Common misconceptions are worth addressing:
“Python is slow” Python orchestrates underlying code. In most applications, performance-critical paths live in native libraries. Only in a small number of domains — such as ultra-low-latency systems — does Python itself become the limiting factor.
“Python is a scripting language” Historically true, as that’s how it originated, but it has evolved vastly since then.
“Python is interpreted” Better to say it is pre-compiled to bytecode that is then executed by a virtual machine.
A better language classification
A proper taxonomy therefore looks like this:
Standalone native languages C, C++, Rust, Zig, Fortran → the binary is the product 1.
Runtime ecosystems Python, JVM languages, .NET languages → the runtime is the product 1.
Host-bound scripting languages Bash, PowerShell, VBA → the host environment is the product
Python belongs firmly in the second group.
A brief note for Rust and Go proponents
A common challenge from Rust or Go developers is that Python’s role is better served by “doing it properly” in a compiled language from the start.
That view makes sense — if your problem is well-specified, stable, performance-critical, and worth committing to upfront architectural constraints. In those cases, Rust or Go are often excellent choices - although these languages are more specialized than, say, C#, F# or JavaScript.
But many real-world problems do not start that way. They begin as ill-defined, exploratory or evolving systems: data pipelines, research code, internal tools, integration glue. A research-team needs to test an idea quickly in a small-scale way, rather than performantly on terabytes of data. A business-development team needs to solve a problem quickly and tactically, because the business needs a solution "yesterday". Some problems move to fast to wait for a strategic solution, or the cost of a strategic solution cannot yet be justified as too much is unknown. In those contexts, early commitment to strict typing, memory models or concurrency primitives can slow learning rather than accelerate it.
Python’s advantage is not that it replaces C#, Java, Rust or Go. It is that it defers commitment. You can explore the problem space quickly, validate assumptions, and only later decide which parts deserve the cost of rewriting in a compiled language. Your Proof-of-Concept or Prototype written in Python becomes your teacher and learning exercise.
In practice, Python and Rust and Go are not competitors but complements: Python for orchestration and discovery; Rust or Go for stabilised, performance-critical components where very specific issues need to be solved. Rust eliminates entire classes of bug to do with memory-management and threading; Go is superb at server-side services. These are not everyday programming needs.
Summary
Python isn’t confused, incoherent or a "toy" language. It simply departs from the mental models of earlier generations of languages and fulfills a unique role that no other language can quite match.
Python is not any of compiled, interpreted or “just a scripting language”. It’s a runtime-centric orchestration layer and a complete ecosystem of its own. It’s the Visual Basic and VBA of the internet era: ideal for rapid assembly, experimentation and leverage rather than purity or control.
And that makes it incredibly useful - and wildly popular.