当前位置: 首页 > 科技观察

Python到JavaScript的编译器,我的天!还能转换代码,怎么做

时间:2023-03-14 17:06:13 科技观察

这类日常的JavaScriptweb开发工具想要成为一个有吸引力的替代品,至少要满足以下三个要求:1.从用户的角度看,创建的网站和app使用工具和原生用JavaScript编写的应用程序在外观、页面加载时间、页面启动时间和持续速度方面应该没有区别。2、从开发者的角度,希望通过这些工具无缝接入其他JavaScript库,进行高效调试。3.从商业的角度来看,应该有大量的开发者愿意接受工具的专业培训,并在企业中使用。学习工具所花费的时间应该很好地转化为生产力,使用该工具创建的应用程序足以满足不断变化的需求。这种类型的转换工具要想成功,必须满足以上三个要求。编译器试图在这三个要求之间取得平衡。对于日常生产环境中使用的编译器,这些方面都不能忽略。就Transcrypt而言,所有三个要求在特定的Transcrypt设计决策中都发挥了决定性作用。要求1:网站和应用程序的外观与使用的底层JavaScript库直接相关。因此,要具有相同的外观和感觉,站点或应用程序必须正确使用相同的软件库。虽然快速的网络连接可能会隐藏差异,但即使对于在公共网络或托管主机上运行类似大小代码的移动设备,也能实现相同的页面加载时间。这使得不可能在每次加载新页面时都下载编译器、虚拟机或更大的运行时。只有在服务器端将代码静态预编译为JavaScript时,才能实现与使用原生JavaScript相同的页面启动时间。页面中所需的代码量越大,差异就越明显。要实现相同的持续速度,必须生成高效的JavaScript代码。鉴于JavaScript虚拟机针对常见编程模式进行了高度优化,生成的JavaScript应该类似于手写的JavaScript,而不是模拟堆栈机器或任何其他底层抽象。需求2:要实现对所有JavaScript库的无缝访问,Python和JavaScript必须使用一致的数据格式、一致的调用模型和一致的对象模型。一致的对象模型需要将JavaScript基于原型的单继承机制与Python基于多继承的机制相融合。应该注意的是,JavaScript最近添加的关键字“class”对于弥合这种根本差异的需要完全没有影响。为了高效调试,设置断点和逐步执行代码等操作必须在源代码级别完成。也就是说,源码图是非常有必要的。一旦遇到问题,您需要通过检查生成的JavaScript代码来找出原因。因此,生成的JavaScript应该与Python源代码同构。利用现有技术意味着源代码必须是纯Python,而不是语法有所改变的变体。一个健壮的实现是使用Python的本地解析器。同样,语义必须是Pythonic,这一要求会产生实际问题,需要引入编译器指令以保持运行时效率。需求三:为了保护企业对客户端Python代码的投资,工具需要持久化。一个持续可用的客户端Python编译器应该具有良好的一致性和出色的性能。如何保持两者的平衡是编译器设计中最关键的部分。Python连续三年成为最佳计算机科学入门课程的教学语言,这一地位足以确保训练有素的Python开发人员的持续可用性。我们能想到的每一个后端计算领域都用到了Python。如果浏览器端编程可以用Python实现,那么所有的Python开发者都可以进行浏览器端编程。这些开发人员设计了大型的、长时间运行的系统,而不是孤立的、短期运行的前端脚本代码。在生产力方面,Python在保持程序运行时性能的同时显着提高了输出,这已经得到从其他编程语言转向Python的开发人员的认可。对于那些关键的操作,比如数值处理和3D图形处理,它们使用的库已经被编译成本地机器代码,这就是Python可以保持运行时性能的原因。最后但并非最不重要的一点是,对不断变化的需求持开放态度意味着支持所有级别的模块化和灵活性。基于类的面向对象编程为此做出了很大贡献,提供了多重继承和复杂的包和模块机制。此外,开发人员可以在不更改现有代码的情况下通过使用命名参数和默认参数来更改调用签名。比较一致性和性能:语言收敛发挥作用一些Python构造与JavaScript构造非常相似,尤其是在转换为最新版本的JavaScript时。两种语言之间有明显的趋同。具体来说,越来越多的Python元素被融入到JavaScript中,例如:for...of...、类(以有限的形式)、模块、解构赋值和参数传播。因为JavaScript虚拟机有for...of...等高度优化的组件,将此类Python组件转换为最匹配的JavaScript组件是有好处的。这样,同构转换生成的JavaScript代码可以受益于目标语言的优化机制,也易于阅读和调试。虽然Transcrypt中的很多调试是通过sourcemaps在Python中一步一步进行的,而不是在JavaScript代码中进行的,但是该工具不应该隐藏底层技术,而应该揭示底层技术,让开发者能够充分了解“真相”事”。这是更可取的,因为如果使用编译器指令,则可以将本机JavaScript代码插入Python源代码中的任何位置。下面是一个使用多重继承的代码片段,显示了Python和Transcrpyt转换后的JavaScript代码之间的同构。原始Python代码为:转换后的JavaScript代码为:专注于同构转换转换的局限性存在于细微之处,有时两种语言的差异是棘手的。例如,“+”运算符在Python中可以用来连接列表,但如果在JavaScript中同构使用“+”运算符,不仅会把列表转换成字符串,还会把字符串粘在一起。当然,a+b可以转换为__add__(a,b),但由于a和b的类型在运行时不确定,这将导致函数调用和动态类型检查代码。再举一个例子说明如何解释“真实”。空列表的布尔值在JavaScript中为True(或true),但在Python中为False。为了在应用程序中全局处理这个问题,需要对每个if语句进行转换,因为在Python构造ifa:中无法确定a是布尔值还是列表或其他类型。所以ifa:必须转换为if(__istrue__(a))。如果在内循环中这样使用,它会再次导致性能不佳。在Transcrypt中,嵌入在代码中的编译指令(即pragma)用于编译本机控制此类工件。这允许使用标准数学符号编写矩阵计算,例如M4=(M1+M2)*M3,而不会为perimeter=2*pi*radius等语句产生任何额外开销。从句法上讲,pragma只是在编译时执行对__pragma__函数的调用,而不是在运行时。导入包含def__pragma__(directive,parameters):pass的存根模块允许此代码无需修改即可在CPython上运行。此外,pragma可以放在注释中。统一类型系统,同时避免命名冲突Transcrypt统一了Python和JavaScript的类型系统,而不是让它们彼此相邻并即时转换。数据转换需要一些时间,增加目标代码的大小和内存使用,增加垃圾收集的负担,并使Python代码和JavaScript库之间的交互难以处理。因此,Transcrypt的决定是拥抱JavaScript世界,而不是创造一个平行世界。下面提供了一个使用Plotly.js库的简单示例:可选的pragma语句,它允许字典键省略引号,只是为了方便。除此之外,该代码看起来与相应的JavaScript代码非常相似。您可以注意到代码如何使用列表推导式,这是JavaScript中仍然缺乏的功能。开发者无需关心Python字典的字面量是如何映射到JavaScript字面量对象的,在编写Python代码时可以使用Plotly.js文档。转换不会在幕后发生。在任何情况下,Transcrypt字典都是一个JavaScript对象。统一类型系统时会出现命名冲突。例如,Python和JavaScript字符串都有一个split()方法,但两者的语义却大不相同。类似的冲突情况还有很多,Python和JavaScript还在不断进化,以后还会有其他的冲突。为了解决这个问题,Transcrpyt支持别名的概念。在Python中使用.split时,它会被翻译成具有Python拆分语义的JavaScript函数.py_split。在原生JavaScript代码中,split是指原生JavaScript的split方法。可以从Python调用JavaScript的本机split方法,在这种情况下,它将被称为js_split方法。虽然在Transcrypt中为此类方法预定义了可用的别名,但开发者可以自定义新的别名或取消现有别名的定义。这种方法解决了统一类型系统引入的所有命名冲突,没有运行时损失,因为别名是在编译时完成的。别名还允许从Python标识符生成JavaScript标识符。例如,在JavaScript中允许$符号作为名称的一部分,但在Python中则不允许。Transcrypt严格遵循Python的语法,使用原生的CPython解析器进行解析。语法与CPython相同。JQuery代码的一部分如下所示:由于Transcrypt使用编译而不是解释,为了允许添加涉及所有模块的缩小和交付,必须在编译之前识别导入的库。为此,Transcrypt还支持C风格的条件编译,可以在下面的代码片段中看到:JavaScript版本并保持向后兼容性。在某些情况下,优化优先于同构:一些优化是可选的,例如启用调用缓存。这导致直接而不是通过原型链重复调用继承的方法。静态类型与动态类型:脚本语言日趋成熟人们重新认识到静态类型的好处,TypeScript就是一个很好的例子。与JavaScript不同,静态类型语法是Python语言的组成部分,Python的原生解析器支持静态类型语法。但是类型检查本身就留给了第三方工具,其中最著名的是mypy。这是JukkaLehtosalo的项目,Python的创建者GuidovanRossum也是该项目的贡献者。为了在Transcrypt中高效使用mypy,Transcrypt团队还为项目贡献了一个轻量级的API,无需通过操作系统直接从另一个Python应用程序激活mypy。虽然mypy仍在开发中,但它已经可以在编译时捕获相当多的拼写错误。静态类型检查是可选的,可以通过插入标准类型注释在本地激活。使用注解的一个例子是mypy的in-processAPI:如上例所示,可以在适当的地方使用静态类型。在上面的示例中,它被用于run函数的签名,因为它是API模块的一部分并且可以被其他开发人员从外部看到。如果有人误解了API参数类型或返回类型,mypy将明确给出一条错误消息,指出产生不匹配的文件和行号。动态类型的概念仍然是Python和JavaScript等语言的核心,因为它允许灵活的数据结构并有助于减少执行任务所需的代码量。源码量很重要,因为要理解和维护源码,必须先通读代码。从这个意义上讲,要实现同样的功能,100KB的Python源码优于300KB的C++源码,阅读可能使用模块的类型定义、显式类型检查和转换代码、重载的构造函数和方法没有难度,抽象基类来处理多态数据结构和类型依赖性。对于单个程序员编写的源代码在100KB以下的小脚本,动态类型只有优势,因为只需要很少的规划和设计,一切都在编程中水到渠成。但是,当应用程序变得太大而无法由个人构建并需要团队时,平衡就会发生变化。对于这样一个以源代码大于200KB为特征的应用程序,缺乏编译时类型检查会导致以下后果:问题需要努力。代价高昂,因为这些错误会影响更多已编写的代码。由于缺少类型信息,模块接口可以用多种方式解释。这意味着更多的开发时间花在了团队成员之间的协商上,以便正确使用API。特别是在大型团队中工作时,动态类型接口会导致不必要的模块耦合。我们需要一个定义良好的稀疏接口。如果参数指向复杂的动态类型对象结构,即使只有一个参数的接口也不能保证稳定的关注点分离。虽然这种“4W”(WhodidWhat,WhyandWhen)编程模型带来了极大的灵活性,但也会导致设计延迟,影响大量现有代码。应用“耦合与内聚”范例。模块在设计决策上可以有强耦合,但模块最好是松耦合的,一个改变模块内部结构的设计决策不应该影响其他模块。基于以上原则,在动态类型和静态类型之间进行选择时可以参考以下经验法则:在特定模块内,设计决策允许耦合。将模块设计为具有内聚性的实体可以减少源代码的数量,并便于对各种实现进行试验。为此,动态类型是一种有效的方法,它可以以最小的设计时间开销换取最大的灵活性。在模块之间的边界处,开发人员应该就要交换的确切信息制定稳定的“合同”。通过这种方法,开发人员无需不断协商即可并行工作,并且他们的目标是固定的,不会改变。静态类型符合这些要求,就API可以接受的交互信息提供正式的、机器可验证的协议。因此,虽然当前静态类型的激增看起来像是倒退,但事实并非如此。动态类型已经占有一席之地并且不会消失。反之亦然,像C#这样的传统静态类型语言也吸收了动态类型的概念。但鉴于使用JavaScript和Python等语言编写的应用程序越来越复杂,有效的模块化、协作和单一验证策略变得更加重要。脚本语言日趋成熟。为什么客户应该选择Python而不是JavaScript由于Web编程的巨大流行,JavaScript也得到了大量的关注和投资。在客户端和服务器上使用相同的语言有其明显的优势。优点之一是随着应用程序规模的增长,代码可以从服务器端移动到客户端。另一个优势是概念的一致性,它允许开发人员在前端和后端工作,而无需不断地在技术之间切换。Node.js等平台的流行是由于希望减少应用程序的客户端和服务器之间的概念距离。但与此同时,这也将当前Web客户端编程的“一刀切”风险扩展到了服务器端。有些人认为JavaScript是一种足够好的语言。最近的版本将开始支持基于类的面向对象(一种对原型内脏的装饰)、模块和命名空间等功能。随着TypeScript的引入,使用严格类型成为可能,尽管要将其集成到语言标准中还需要很多年。即使具有这些特性,JavaScript也不会成为所有其他语言的终结。关于JavaScript的一些夸张浏览器语言市场需要多样性,事实上所有的自由市场都需要多样性。这意味着我们可以为手头的工作选择合适的工具,用锤子代替钉子,用螺丝刀代替螺丝。Python从一开始就以清晰、精确和可读性为标准进行设计。它的价值不容小觑。在未来很长一段时间内,大多数客户端编程可能仍将是JavaScript。但对于考虑替代语言的开发人员来说,影响持久性的是语言的动态,而不是语言的实现。所以最重要的是使用哪种实现,而不是选择哪种语言。为此,Python无疑是一个有效且安全的选择。Python大受欢迎,越来越多的浏览器在其实现中考虑使用Python,Python在保持性能的同时也越来越接近CPython的黄金标准。