If you are a physicist, chemist, material scientist, or any other type of researcher involved with modeling things in computers, chances are you already know some Python.

While Python is highly criticized in some circles, there is no question it has been massively successful in the scientific community.

In this piece I will put forward some reasons why I think Python is uniquely suited to research. I will focus more on the physics/material science community, as I know it better, but I believe a lot of what I write should apply to other communities.

Coding as a researchers

From what I have seen being part of it, the physics community has a very specific relation to programming. Unlike other coders, we do not care too much (or sometimes at all) about the code itself. The result is often the only thing that matters. Sometimes, even the exact result is not too important, as the interpretation of compared results is the real product of the research. A lot of code is one-off, write it, run it, and forget it. As a consequence, bad code, slow code, ugly code, unmaintanable code, can, and will, be written and used without much consequences on “the science”.

On the other hand, code that gets reused is also under a weird constraint. The maintainers, often PhD students, will change every two or three years. Said maintainers also have varying levels of programming knowledge, and on average quite low software architecture knowledge. That is the natural consequence of many scientists being self-taught coders with low interest in the programming art itself.

As a result, our community needs a language that is:

  • easy to learn for students
  • expressive enough to allow experimentation and fast iteration
  • restrictive enough that there are not as many solutions to a given problem as there are PhD students
  • make reading someone else code easy

Looking back at the legacy

Many programs from the physics community, publicly available or not, have historically been written in Fortran.1 The reasons for Fortran longevity (counting all the early iterations, the language is 68 years old in 2025) are a mix of early adoption, initial superiority of the compilers,2 and later limited knowledge of other languages combined with a particularly nice set of language features for numerical computation.3

While modern Fortran is not as bad a language as the internet would let you think, it is still a pretty quirky language that makes some tasks, such as text manipulation pretty painful. It also lets you shoot yourself in the foot more than you would think when misusing some features such as in and out intents and arrays.

Python’s rise

With the advent of Python as a teaching language and the development of the three pillars, matplotlib, numpy, and scipy, most of the community has been moving toward Python for the one-off tasks, but also for some more long-lasting software.

While most CPU intensive software are still often written in Fortran if numpy+scipy is not suited to express them, we also see a rising number of software being written with a core of C++ or C designed as a Python extension, letting the user stay in the Python world.4

Python is particularly suited to our community because:

  1. It allows you to become proficient fast and with little effort, which is important because programming is almost a distraction from the real science for many researchers. The language is relatively simple (few concepts to learn) and abstracts away a lot of the complexity of programming (particularly management of resources such as memory and file handles). The performance cost of these abstractions is rarely a problem (until it is; see point 3).

  2. The large ecosystem and expressive language let us write the one-off programs in few lines and rely on pretty healthy abstractions such as numpy, pandas, matplotlib, networkx… A task-specific post-processing script can be written in a few minutes. A more reusable program can be written in a few hours.

  3. When performance becomes a problem, the excellent FFI (Foreign Function Interface) allows the existence of high-performance libraries (numpy, scipy, igraph…) to be used. When no off-the-shelf solution exists, many tools let us build the missing parts with technology we know, such as Cython and numpy.f2py.

Future of scientific computing

Today, it looks like the future of computing is in GPUs. Python already captured that field, thanks to Tensorflow, Pytorch and JAX, as well as good bindings to lower-level libraries such as CUDA and OpenCL. Again, the unique properties of Python (in particular its ease-of-use and great FFI) made it a natural choice for the growing AI community that drove the transition toward GPUs.5

On an other note, Julia is clearly challenging the status-quo, with a language that offers expressivity and flexibility on-par with Python (if not better) but amazing performances thanks to an almost magical JIT compiler.6 I wish them all the luck as, in my experience, naive Julia is basically as fast as my best numpy, which shows how superior their approach is. The language also enables some pretty cool meta-programming features including auto-differentiation, which means some physics-related code can be implemented as in the papers, without manually deriving the derivatives.7 They will still have a hard time overcoming the accumulated inertia of Python though.

Addressing common issues

Python has some obvious limitations that makes it completely unsuitable for applications such as embedded systems. However, it appears to me that these problems are not that much of a problem in the case of scientific computation.

Low performances

Python is slow. To be more accurate, algorithms written in pure Python using Python primitives can be up to 100x slower than equivalent C implementation. The reason is that any Python operation does a lot in the background: unicode handling, memory allocation, and many many hash-map lookups.8

I would argue that this is rarely a problem.

For one, if the Python program runs in 0.5 s and the Fortran program runs in 1 ms, and you are running said program twice a day, the performance gain is completely negligible compared to both writing time, and compilation time.

Then, I already mention the amazing ecosystem and FFI. Python really shines as a glue language between high-performance chunks. If you can express the hot loop in terms of some high-performance C or C++ library and just do the rest in Python, it is worth it.

I recently chose to write some hotloop of a small package in Cython to great effect. I made the prototypes in Python to validate the algorithm, then ported that to Cython to reach the best performances. Most of the package is written in Python, and I also keep the Python version of the hotloop as a reference implementation. The main advantage of Cython over C is the easy integration with numpy and other python objects.

If none of these points apply to your case, or if you would rather avoid a multilanguage code base,9 it may be time to reach for C, C++, Fortran or Rust.

Parallel programming

A common concern with Python is the infamous “GIL” (Global Interpreter Lock). This lock prevents multiple OS threads from running Python code in parallel in the same interpreter. I would argue that it is also rarely a problem.

First, the GIL is actually protecting us from the hell that shared-memory parallel programming is. Debugging race conditions is not fun, and you and I have more important things to do.

Second, there are many cases where the problem is parallelizable enough that it can be split into independent chunks. In that case, process parallelism is well suited and truly parallel (each process has its own GIL). I particularly recommend learning about multiprocessing.Pool and multiprocessing.Pool.imap_unordered.

Finally, CPython is now experimenting with removing the GIL, although the process will be long and progressing, as removing it will break a lot of code that was benefiting from the protection the GIL offers against race conditions.

Beside, in my experience, few people actually understand threads vs. processes in the community. Many people still only know about OpenMP and MPI and are very confused about the underlying OS primitives.

If you do understand threads and are working on a problem that genuinely requires shared-memory parallelism, then I would recommend that you use Rust or Go as these languages provide good primitives for handling parallel computing without shooting yourself in the foot.

Type system

There is a lot of confusion about Python’s type system. The accurate way of qualifying it is to call it strong, structural, and dynamic. Dynamic means that variables are not bound to a specific type, strong means that values have a specific type, and structural means that if two values of different types implement the same subset of operations/attributes, they can be used in the same context.

The strong part is what makes Python so much nicer to work with than some other scripting languages like Perl or JavaScript, that do all sorts of weird conversions to compensate for mixing incompatible types.

The dynamic part is probably the one that causes the most problems to users, but it is also very useful. This is very useful for incremental prototyping; the type system stays out of the way as you experiement with various algorithms or ways to represent data. The main drawback is that when the codebase becomes large enough, a change of data representation in some function can cascade into issues much further without triggering any bells or whistles to alert you.

The extensive use of assert (defensive programming) and type annotations (declarative programming) makes this a lot less of a concern. Just don’t forget to run the type checker once in a while.

Honorable mentions

Packaging has been, and still is a thorn in Python’s side. The ecosystem is evolving for the better, but I would need an entire article to address that issue. Recently my short answer has been: “use uv and uv env”.

Complains about indentation are common, but I never found them compeling. Syntax is the least interesting part of a language in my opinion, a mere detail of design when compared to semantic and ecosystem. I consider indentation absolutly necessary for readability irrespective of the language, so using it as part of the program structure feels natural.

Conclusion

Python is the best language for a large majority of our work as scientists. It is easy to learn, easy to transmit, and its ecosystem makes making high performance computations easy. I believe it played a major role in moving computational science forward and reducing barriers to entry to theoretical physics and chemistry. As a programming language nerd, I will not be sad if a new language comes to replace it, but it will have to be very good to achieve that.


  1. FORTRAN 77 is fortunately almost dead, but Fortran 90/2003/2008 is still in use in the community, mostly for legacy software, but also rarely for new code. ↩︎

  2. Historically, the Fortran compilers were just better than the C compilers because they were purpose-built and more work had been done on them. Today, all Fortran compilers share a backend with a C compiler (gfortran/gcc, flang/clang, ifort/icc) and have mostly the same performances. That being said, Fortran still offers some opportunities for optimizations by default that are only opt-in in C (mostly related to pointer aliasing, that require declaration of intent in C, using qualifiers such as const, volatile and restrict). ↩︎

  3. Fortran has array operations, which while present in some higher level languages such as R, Matlab and Python, has been absent from high performance languages for a very long time. They are now found in some more recent languages such as Chapel (which specifically targets replacing Fortran in HPC), Julia (which is also deeply rooted in the numerical computation domain) and Odin (which is more oriented toward game programming). ↩︎

  4. I can mention gpaw and pyscf for the electronic structure community. I am sure every other community has their examples. ↩︎

  5. Interestingly enough, Torch was initially interfaced to the only language I would consider having a better FFI than Python, Lua. However, the more limited expressivity of the language, or most-likely the community already being used to python, caused the authors of Torch to switch to Python. ↩︎

  6. Built-in Python functions may be quite fast on the other end, as they are written in C and often with very good algorithms. The sorting algorithm comes to mind, as it is excellent in non-pathologic, reaching linear speed, and state-of-the art good in other cases (using quicksort). ↩︎

  7. The Density Functional Theory code DFTK.jl is a shining example of everything cool about Julia applied to concreate physics. ↩︎

  8. Because other aspects of the languages have been prioritized, no real effort had been made on the interpreter’s performance for a long time. This came to an end starting with Python 3.11 thanks to the faster-python project at Microsoft (leaded by Guido Van Rossum himself). A major milestone is the introduction of a JIT compiler in Python 3.13, which will be the foundation for future performance optimizations. ↩︎

  9. Although, no Fortran or C or C++ codebase is truly single language, as you will at least have some sort of build system. I would rather deal with Python+C than C+CMake. Rust seems pretty good at being self contained. ↩︎