Well, what about "FROM python:3.18" and using requirements.txt or something like that? I mean, running an arbitrary Python version will get you in trouble anyway.
There's no mechanism to enforce this is done consistently. With nix, there is.
The degree to which this guarantee is useful or necessary depends on your use case.
Depends on the repository used, actual version revision of Python, compiled features, the way the requirements for is written, the way the current version of pip resolves them, the base is that Python image and a lot of other things. Doing that gives you maybe 30% of the way towards something reproducible and consistent.
``` FROM python:latest ADD . RUN pip install foo ```
If I run this today, and I run this a year from now, I'm going to different versions of `python` and `foo` and there is no way (with just the Dockerfile) to know which version of `foo` and `python` were intended.
Nix on the other hand, forces me to use a git sha[^0] of my dependency; there is no concept of a mutable input. So to your point it's hard to 'upgrade' from version a -> b in a controlled fashion if you don't know what `a` even was.
[0]: or the sha256 of the depedency which yes, I understand that's not easy for humans to use.