But things get much more complex very fast, and then the compiler requires the programmer to tell it what types things are.
Then there is the approach taken by JavaScript engines like V8. Run the code for a while, see what the types of variables are, then compile a customized, much more efficient version of the code to speed things up. Still need a fallback, though, in case a totally different kind of value gets stuck in a variable than the type at the time the optimized code was generated. Nothing in JavaScript (or Python) prevents a programmer from assigning any kind of value into a variable, though, so the engine must be prepared for the types of variables to change in unexpected ways.
(The reason the state of the art for these techniques is in the JavaScript engines is because the organizations behind the major browsers pour so many resources into making them fast.)
Now, if you can figure out a way to always predict the types of every variable in a JavaScript or Python program without actually running it, pretty much any CS program would be willing to give you a tenured position on their faculty on the spot. :)
You certainly can compile languages this dynamic, the problem is that the "simple" way of compiling it doesn't result in very fast code, because you essentially end up mimicking what the interpreters/vms do very closely, and a lot of what ends up being executed will be things like method lookups which are reasonably well optimized in the interpreters.
The problem boils down to the ability to "change the world" at almost any point.
E.g. in Ruby (don't know about Python), you can come back from an eval() call (or load or require) and find that the expression 1 + 1.0 results in the string "two", or will send you an insulting e-mail, because you can even override Fixnum#+.
Now, there's a lot that can be done to infer typing for these types of languages well enough that you can get away with slow-path fallbacks for the rare cases where users does something stupid (like override Fixnum#+) and run faster most of the time, but it's a lot of extra effort.
The short version is "no, it shouldn't".
The longer version is that, viewing Python's runtime class memberships as "types", the type system it would represent is not one for which inference without explicit declarations is generally decidable, so an AOT compiler for unmodified, unrestricted Python that is expected to infer types at runtime simply would not be possible.
You could AOT compile it into C code with a lot of runtime metadata and indirections -- but that would probably have worse performance than the existing interpreter.
> Way back when, you could get a BASIC compiler to turn your interpreted BASIC code into assembly.
Well, yeah, BASIC is actually structurally quite close to assembly. Its not a dynamically-typed multiparadigm language like Python.
Its actually quite easy to be worse than an interpreter that is actually a combination of a bytecode compiler and a VM built around the execution model of the language it is for, which is what the main Python interpreter is.
Consider how many people end up implement their own introspectable C object models.
I believe that some code paths critical for performance in Rails use metaprogramming, for example.
I did an experiment in this paper http://dl.acm.org/citation.cfm?id=2647517&dl=ACM&coll=DL&CFI... where I showed that just be rearranging some code you can confuse the Rubinius object allocator into using a slow path for instance variables. That gives you both slow performance, and worse than that, unpredictable performance - the programmer doesn't understand what they've done wrong to trigger that fallback performance.
If you declare a virtual function inline, if it is invoked from a well defined object (ie, not through a reference or pointer) you also get a similar effect while still being able to override, except the code is not a static call but is just inlined with that one implementation of the function.
You just need to use profile guided optimisation.
It provides facilities for all of python abilities regardless of what the program is going to do.
Now, there are subsets of python people have developed that can be compiled and all sorts of other stuff.
Beyond that, I'd just say "all this stuff sounds easy but gets much harder when you actually have to program it".
"Why can't it just know!" is the programmer's lament.
Read input from a file or stdin. Eval that input. What type does it return?
Etc.
If I'm doing something like counting from 1 to 10 and summing the count, why can't this be compiled to run the same speed as C?
Obviously since it's interpreted, but when I'm done with development, why can't I just run it through a compiler to get fast assembly code?
Way back when, you could get a BASIC compiler to turn your interpreted BASIC code into assembly.
What fundamental am I missing?