这不是什么秘密,我想要一个WebAssembly的Python实现。这不仅可以让Python进入浏览器,而且由于iOS和Android都支持将JavaScript作为应用程序的一部分运行,这也将使Python进入移动设备。这一切都让我兴奋。
但是当我想到创建一个新的Python实现这项艰巨的任务时,我的大脑也开始问这样一个问题:Python到底是什么?我们与CPython相处的时间太长了,我怀疑我们中的大多数人只是简单地认为Python==CPython";。PyPy试图做到如此兼容,以至于他们将实现CPython的实现细节。基本上,我所知道的大多数Python实现都努力通过CPython的测试套件,并尽可能地与CPython兼容。
这令人望而生畏。CPython实现的Python是非常动态的,并且公开了许多只有在您以某种方式使用解释器实现Python时才有意义的东西。例如,PyPy有一个可供他们JIT使用的基线解释器,但是在Python中有很多东西可以迫使PyPy关闭JIT而坚持使用字节码。REPL本身就使事情变得非常动态,因为您输入到REPL中的每一件事都是由解释器当场动态解析、编译和执行的。
这让我开始思考Python到底是什么的问题。语言的核心是什么使它成为现在的样子?所有Python实现需要覆盖什么基线,才能真正将自己称为人们仍能识别的Python实现?或者从我的角度来看,要将Python直接编译到WebAssembly并仍然被认为是Python实现,需要实现多少?
真正让我开始思考这个问题的是,当我开始考虑如何将Python编译成WebAssembly时?不实现另一个解释器,但实际上从Python源代码发出静态WebAssembly,并且仍然合理地将其命名为Python。
我知道的一件事是,通过eval()或Compile()进行动态编译可能并不容易,因为WebAssembly的安全模型会在加载时验证模块。这表明在其他代码的内存空间中没有被构造为只运行任意代码,这可能会使实现REPL变得棘手。
但这让我思考:Python真的需要REPL吗?不要误会我的意思,它非常方便,但是如果一个实现没有REPL,它会不会不再是Python呢?我会争辩说,没有REPL的Python仍然是Python,只是缺少一个(潜在的关键)特性。
这让我走上了思考Python的哪些部分需要考虑的道路?
没有当地人,你能活下去吗()?能够任意地将所有定义的局部变量及其值收集到字典中是一件非常动态的事情。如果您使用的是像CPython这样的解释器,那么您只需将当前执行框架中的一些内容组合在一起,就可以得到本地变量。但在编译语言中,这需要更多的工作,因为您必须知道何时收集所有这些信息,因为当调用locals()时,这些信息不一定只是闲置。
或者,如果人们凌驾于当地人()本身呢?同样,这在CPython中也不是什么大问题,因为内置模块有一个__dict__,您可以覆盖它,并且它会简单地向下传播到任何未来的调用。但是在编译语言中,执行这类检测需要付出更多的努力,而且执行这样的检查最终会耗费大量的性能。
sys.settrace()怎么样呢?它实际上触发每个字节码的回调,如果编译代码,这并不是很有效。您可以通过检查是否在每行之后设置了跟踪函数来伪造它,但是如果您大多数时候不设置这样的钩子,这似乎有点过了(不过,它可能是要在这种支持下编译的编译器标志)。
那么sys._getframe()怎么样呢?编译后的语言不一定会直接访问执行框架,因此您会费心模拟这一点吗?由于任何函数都可以请求执行帧,因此您需要始终做好按需提供执行帧的准备。
正如您所看到的,Python中有很多东西使得编译变得困难(因此,Nuitka接受这一挑战的能力更强)。但是我愿意打赌我上面提到的东西你99.9%的时间都不用,所以如果一个实现遗漏了它们,它还能被认为是Python吗?
这个问题我没有很好的答案。但它的答案决定了实现Python有多难,以及它与现有软件的兼容性有多强。我要说的是,我认为WebAssembly不需要支持大量的Python软件就能发挥作用。WebAssembly可以访问其他语言生态系统,如Rust和JavaScript,因此使用其他语言实现您需要的内容的可能性绝对高于零。
开发一个编译器将Python代码直接转换为WebAssembly并牺牲一些兼容性来提高性能可能是有意义的。开发一个针对WebAssembly设计但与现有代码保持很好兼容性的解释器可能是有意义的。在他们的WebAssembly工作中简单地支持RustPython可能是有意义的。也许皮迪德会带我们去那里。我不认为这些可能性中的任何一种本质上都是错误的,它可能只会归结为哪种可能性足以激发人们的兴趣,使其对他人有用。