当前位置:首页 > 数码 > 兼具解释型和编译型特性的言语-Python (具备解释)

兼具解释型和编译型特性的言语-Python (具备解释)

admin4个月前 (05-09)数码24

不知道有没有小同伴跟我一样,刚开局学习/target=_blankclass=infotextkey>Python的时刻都据说过Python是一种解释型言语,由于它在运转的时刻会逐行解释并口头,而C++这种是编译型言语

图片

不过我当天看到了一篇文章,作者提出Python其实也有编译的环节,解释器会先编译再口头

岂但如此,作者还以为【解释】与【编译】是失误的二分法、限度了编程言语的或许性。Python既是解释型言语,也是编译型言语!

本文文字较多,干货满满,耐烦看完置信你会有不小的收获

原文:

具备解释

前言

本文所说的Python,不是指PyPy、Mypyc、Numba、Cinder等Python的代替版本,也不是像Cython、Codon、mojo1这样的类Python编程言语

我指的是惯例的Python——CPython

目前,我正在编写一份教材,教在校生如何阅读和了解程序报错信息(programmingerrormessages)。咱们正在为三种编程言语(C、Python、)开设课程

程序报错信息的实质的关键点之一在于程序报错是在不同阶段生成的,有些是在编译时生成,有些是在运转时生成

第一门课是针对C言语的,详细来说是如何经常使用GCC编译器,以及展示GCC如何将代码转换成可口头程序

除此之外,这节课还讨论了在上述阶段或许出现的程序报错,以及这些报错将如何影响所出现的失误信息。关键的是:早期阶段的失误将阻止在前期阶段检测到失误(也就是说A阶段的报错出现之后,B阶段就算有失误也不会检测进去)

当我将这门课调整成针对Java和Python时,我发现Python和Java都没有预处置器(preprocessor),并且Python和Java的链接(linking)不是同一个概念

我疏忽了上方这些变动,但是我偶然发现了一个幽默的现象:

编译器在各个阶段会生成报错信息,而且编译器通常会在继续口头之前把前面阶段的报错显示进去,这就象征着咱们可以经过在程序中故意创立失误来发现编译器的各个阶段

所以让咱们玩一个小游戏来发现Python解释器的各个阶段

Whichisthefirsterror?

咱们将创立一个蕴含多个bug的Python程序,每个bug都试图引发不同类型的报错信息

咱们知道惯例的Python每次运转只会报告一个失误,所以这个游戏就是——哪条报错会被首先触发

图片

每行代码都会发生不同的报错:

疑问在于,哪个失误解先被显示进去?须要留意的是:Python版本很关键(比我构想的要关键),所以假设你看到不同的结果,请记住这一点

PS:上方运转代码所经常使用的Python版本为Python3.12

在开局口头代码之前,先想想【解释】言语和【编译】言语对你来说象征着什么?

上方我将给出一段苏格拉底式的对话,宿愿你能反思一下其中的区别

苏格拉底:编译言语是指代码在运转之前首先经过编译器的言语。一个例子是C编程言语。要运转C代码,首先必需运转像orclang这样的gcc编译器,而后能力运转代码。编译后的言语被转换为机器代码,即CPU可以了解的1和0。

柏拉图:等等,Java不是一种编译言语吗?

苏格拉底:是的,Java是一种编译言语。

柏拉图:但是惯例Java编译器的输入不是一个.class文件。那是字节码,不是吗?

苏格拉底:没错。字节码不是机器码,但Java依然是一种编译言语。这是由于编译器可以捕捉许多疑问,因此您须要在程序开局运转之前更正失误。

柏拉图:解释型言语呢?

苏格拉底:解释型言语是依赖于一个独自的程序(恰到好处地称为解释器)来实践运转代码的言语。解释型言语不须要程序员先运转编译器。因此,在程序运转时,您犯的任何失误都会被捕捉。Python是一种解释型言语,没有独自的编译器,您犯的一切失误都会在运转时捕捉。

柏拉图:假设Python不是一种编译言语,那么为什么规范库蕴含名为py_compileandcompileall的模块?

苏格拉底:嗯,这些模块只是将Python转换为字节码。他们不会将Python转换为机器代码,因此Python依然是一种解释型言语。

柏拉图:那么,Python和Java都转换为字节码了吗?

苏格拉底:对。

柏拉图:那么,为什么Python是一种解释型言语,而Java却是一种编译型言语呢?

苏格拉底:由于Python中的一切失误都是在运转时捕捉的。(ps:请留意这句话)

当咱们口头上方那段有bug的程序时,将会收到上方的失误

图片

检测到的第一个报错位于源码的最后一行。可以看到:在运转第一行代码之前,Python必需读取整个源码文件

假设你脑子里有一个关于【解释型言语】的定义,其中包括解释型言语按顺序读取代码,一次性运转一行,我宿愿你忘掉它

我还没有深化钻研CPython解释器的源码来验证这一点,但我以为这是第一个检测到的报错的要素是Python3.12所做的第一个步骤是扫描(scanning),也称为词法剖析

扫描器将整个文件转换为一系列标志(token),而后继续启动下一阶段。

扫描器扫描到源码最后一行的字符串字面值末尾少了个引号,它宿愿把整个字符串字面值转换成一个token,但是没有完结引号它就转换不了

在Python3.12中,扫描器首先运转,所以这也是为什么第一个报错是unterminatedstringliteral

咱们把第四行的代码的bug修复好,第123行仍有bug

图片

咱们如今来口头代码,看下哪个会首先报错

图片

这次是第二行报错!雷同,我没有去检查CPython的源码,但是我有理由确定扫描的下一阶段是解析(parsing),也称为语法剖析

在运转代码之前会先解析源码,这象征着Python不会看到第一行的失误,而是在第二行报错

我要指出我为这个小游戏而编写的代码是齐全没无心义的,并且关于如何修复bug也没有正确的答案。我的目的纯正是编写失误而后发现python解释器如今处在哪个阶段

我不知道print()=None或许是什么意思,所以我将经过将其交流为print(None)来处置这个疑问,这也没无心义,但至少它在语法上是正确的。

咱们把第二行的语法失误也修复了,但源码还有另外两个失误,其中一个也是语法失误

图片

回顾一下,语法失误在回合二的时刻优先显示了进去,在回合三还会一样吗

图片

没错!第三行的语法失误优先于第一行的失误

正如回合二一样,Python解释器在运转代码之前会先解析源码,对其启动语法剖析

这象征着Python不会看到第一行的失误,而是在第三行报错

你或许想知道为什么我在一个文件中拔出了两个SyntaxError,难道一个还不够标明我的观念吗?

这是由于Python版本的不同会造成结果的不同,假设你在Python3.8或许更早的版本去运转代码,那么结果如下

在Python3.8中,第2轮报告的第一个失误信息位于第3行:

图片

修复第三行的失误之后,Python3.8在第2行报告以下失误信息:

图片

为什么Python3.8和3.12报错顺序不一样?是由于Python3.9引入了一个新的解析器。这个解析器比以前的naïve解析器性能更弱小

旧的解析器无法提早检查多个token,这象征着旧解析器在技术上可以接受语法有效的Python程序

尤其是这种限度造成解析器无法识别赋值语句的左边能否为有效的赋值指标,好比上方这段代码,旧解析器能够接纳上方的代码

图片

上方这段代码没有任何意义,甚至Python语法是不准许这么经常使用的。为了处置这个疑问,Python曾经存在过一个独立的,hacky的阶段(这个hacky我不知道用什么翻译比拟好)

即Python会审核一切的赋值语句,并确保赋值号左边实践上是可以被赋值的物品

而这个阶段是出现在解析之后,这也就是为什么旧版本Python中会先把第二行的报错先显示进去

如今还剩最后一个失误了

图片

咱们来运转一下

图片

须要留意的是,Traceback(mostrecentcalllast)示意Python运转时报错的关键内容,这里在回合四才出现

经过前面的扫描、解析阶段,Python终于能够运转代码了。但是当Python开局运转解释第一行的时刻,引发一个名为ZeroDivisionError的报错

为什么知道如今处于【运转时】,由于Python曾经打印出Traceback(mostrecentcalllast),这示意咱们有一个堆栈跟踪

堆栈跟踪只能在运转时存在,这象征着这个报错必需在运转时捕捉。

但这象征着在回合1~3中遇到的报错不是运转时报错,那它们是什么报错?

Python既是解释型言语,也是编译型言语

没错!CPython解释器实践上是一个解释器,但它也是一个编译器

我宿愿上方的练习曾经说明了Python在运转第一行代码之前必需经过几个阶段:

旧版本的Python多了一个额外阶段:

让咱们将其与前面编译C程序的阶段启动比拟:

Python在运转任何代码之前依然口头一些编译阶段,就像Java一样,它会把源码编译成字节码

前面三个报错是Python在编译阶段发生的,只要最后一个才是在运转时发生,即ZeroDivisionError:divisionbyzero.

实践上,咱们可以经常使用命令行上的compileall模块预先编译一切Python代码:

图片

这会将以后目录中一切Python文件的编译字节码放入其中__pycache__/,并显示任何编译器失误

假设你想知道那个__pycache__/文件夹中究竟有什么,我为EdmontonPy做了一个演讲,你应该看看!

演讲地址:

只要在Python被编译为字节码之后,解释器才会真正启动,我宿愿前面的练习曾经证实Python确实可以在运转时之前报错

编译言语和解释言语是失误的二分法

每当一种编程言语被归类为【编译】或【解释】言语时,我都会感到很厌恶。一种言语自身不是编译或解释的

一种言语是编译还是解释(或两者兼而有之!)是一个成功细节

我不是惟逐一个有这种想法的人。LaurieTratt有一篇精彩的文章,经过编写一个逐渐成为提升编译器的解释器来论证这一点

文章地址:

还有一篇文章就是BobNystrom的CraftingInterpreters。以下是第2章的一些引述:

编译器和解释器有什么区别?

理想证实,这就像问水果和蔬菜之间的区别一样。这仿佛是一个二元的非此即彼的选用,但实践上水果是一个植物学术语,而蔬菜是烹饪学术语。

严厉来说,一个并不象征着对另一个的否认。有些水果不是蔬菜(苹果),有些蔬菜不是水果(胡萝卜),但也有既是水果又是蔬菜的可食用植物,如西红柿

当你经常使用CPython来运转Python程序时,源码会被解析并转换成外部字节码格局,而后在虚构机中口头

从用户的角度来看,这显然是一个解释器(由于它们从源码运转程序),但假设你细心观察CPython(Python也可译作蟒蛇)的鳞状表皮(scalyskin),你会发现它必需在启动编译

答案是:CPython是一个解释器,它有一个编译器

那么为什么这很关键呢?为什么在【编译】和【解释】言语之间做出严厉的区分会大失所望?

【编译】与【解释】限度了咱们以为编程言语的或许性

编程言语不用由它是编译还是解释来定义的!以这种僵化的形式思索限度了咱们以为给定的编程言语可以做的事情

例如,JavaScript通常被纳入解释型言语类别。但有一段期间,在Chrome中运转的JavaScript永远不会被解释——雷同,JavaScript被间接编译为机器代码!因此,JavaScript可以跟上C++的步调

出于这个要素,我真的厌倦了那些说解释型言语肯定慢的论点——性能是多方面的,并且不只仅取决于"自动"编程言语的成功

JavaScript如今很快了、Ruby如今很快了、Lua曾经快了一段期间了

那关于通常被标志为编译型言语的编程言语呢?(例如C)你是不会去想着解释C言语程序的

言语之间真正的区别

言语之间真正的区别:【静态】还是【灵活】

咱们应该教给在校生的真正区别是言语特性的区别,前者可以静态地确定,即只盯着代码而不运转代码,后者只能在运转时灵活地知道

须要留意的是,我说的是【言语特性】而不是【言语】,每种编程言语都选用自己的一组属性,这些属性可以静态地或灵活地确定,并联合在一同,这使得言语更灵活或更静态

静态与灵活是一个范围,Python位于范围中更灵活的一端。像Java这样的言语比Python有更多的静态特性,但即使是Java也包括反射之类的物品,这无疑是一种灵活特性

我发现灵活与静态经常被一概而论,编译与解释一概而论,这是可以了解的

由于通经常常使用解释器的言语具备更多的灵活特性,如Python、Ruby和JavaScript

具备更多静态特性的言语往往在没有解释器的状况下成功,例如C++和

而后是介于两者之间的Java

Python中的静态类型注释曾经逐渐(呵呵)在代码库中获取驳回,其中一个希冀是:由于更多静态的物品,这可以解锁Python代码中的性能长处

可怜的是,理想证实,Python中的类型(是的,只是普通类型,思索元类)和注释自身都是Python的灵活特性,这使得静态类型不是大伙所希冀的性能长处

最后总结一下:


python语言的特点是什么呢?

python语言的特点主要有速度快、免费、可移植性、解释性、可扩展性等,具体如下:1、速度快:Python的底层是用C语言写的很多标准库和第三方库也都是用C写的运行速度非常快。 2、免费:使用者可以自由地发布这个软件的拷贝、阅读源代码、做改动、把一部分用于新的自由软件中。 3、可移植性:由于其具有开源本质,Python已经被移植在许多平台上,这些平台包括Linux、Windows FreeBSDMacintosh等。 4、解释性:Python语言写的程序不需要编译成二进制代码可以直接从源代码运行程序。 5、可扩展性:Python本身被设计为可扩充的并非所有的特性和功能都集成到语言核心。 Python提供了丰富的API和工具以便程序员能够轻松地使用C语言、C++Cython来编写扩充模块。 达内教育开设Python人工智能与数据分析实战课,因材施教课程设计 满足不同人员学习需求,OMO线上线下同步教学,因材施教分级教学。 想了解更多有关python语言的详情,推荐咨询达内教育。 达内教育具有丰厚的师资力量,优秀的教学体系,教学质量突出,实战讲师,经验丰富,理论知识+学习思维+实战操作,打造完整学习闭环。 达内教育独创TTS8.0教学系统,并设有企业双选会。 达内的OMO教学模式,全新升级,线上线下交互学习,直播学,随时学,随时问,反复学,让学员学习更便捷。 感兴趣的话点击此处,免费学习一下

Python是一种()语言。

Python是一种()语言。 A.编译型语言B.解释型语言C.高级语言D.低级语言正确答案:解释型语言;高级语言

免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。

标签: Python

“兼具解释型和编译型特性的言语-Python (具备解释)” 的相关文章

Python中的LEGB规则 (python怎样打开)

Python中的LEGB规则 (python怎样打开)

Python 中的 LEGB 规则决定了变量和函数的作用域解析顺序。它代表了四个作用域层级: 局部作用域 闭包函数外的函数 全局作用域 内置作用域...

b-b-个入门建议!-Python-技术书籍推荐-附赠-11 (b+b+b等于什么)

b-b-个入门建议!-Python-技术书籍推荐-附赠-11 (b+b+b等于什么)

近年来,Python 持续火爆,越来越多的人开始入门学习 Python。RealPython 作为最受好评的 Python 学习网站,拥有超百万的浏览量,以下是 RealPython 的开发者给...

处置日常义务的终极工具!-Python-文件读写实战 (处置行为是什么意思)

处置日常义务的终极工具!-Python-文件读写实战 (处置行为是什么意思)

/target=_blankclass=infotextkey>Python文件的读写操作时,有很多须要思考的细节,这包含文件关上形式、读取和写入数据的方法、意外处置等。 在本文中,...

Python中的Random模块-摸索随机性的神奇环球 (python编程)

Python中的Random模块-摸索随机性的神奇环球 (python编程)

随机性在计算机编程和数据迷信中表演着至关关键的角色。/target=_blankclass=infotextkey>Python中的random模块提供了丰盛的工具和函数,协助咱们生成随机数...

惰性求值和lambda表达式的强大组合-Python高级技巧 (惰性求值和逻辑短路)

惰性求值和lambda表达式的强大组合-Python高级技巧 (惰性求值和逻辑短路)

Lambda 表达式 在 Python 中,Lambda 表达式是一个匿名函数,它可以在需要函数对象的地方使用。Lambda 表达式的语法如下: lambda arguments: exp...

一份收藏者必备清单-100个精选Python库 (收藏者的心态)

一份收藏者必备清单-100个精选Python库 (收藏者的心态)

/target=_blankclass=infotextkey>Python为啥这么火,这么多人学,就是由于繁难好学,性能弱小,整个社区十分生动,资料很多。而且这言语触及了方方面面,比如智能...

掌握网络世界的无限可能-Python分布式爬虫助力搜索引擎打造 (掌握网络世界的好处)

掌握网络世界的无限可能-Python分布式爬虫助力搜索引擎打造 (掌握网络世界的好处)

主从模式 主从模式是一种简单的分布式爬虫架构,其中一台主机作为控制节点,负责管理所有运行爬虫的从机。 主节点负责向从机分配任务,并接收新生成的任务。从机只需要从主节点接收任务,并把新生...

轻松把握多线程和多进程-Python编程进阶 (多线是什么意思)

轻松把握多线程和多进程-Python编程进阶 (多线是什么意思)

1、简介 咱们将讨论如何应用/target=_blankclass=infotextkey>Python口头多线程和多进程义务。它们提供了在单个进程或多个进程之间口头并发操作的方法。并...