引入

高中有一好友,在大学期间苦练 Java,各类八股文烂熟于心,最终进入某大厂却在维护 Python 项目。

而本人不思进取,不想背八股文,于是大学期间只是苟着写 Python,然而却最终进入某互联网小厂维护 Java 项目。

世事无常,就像老话说得:

人生就像蛋炒饭,你永远不知道自己下一口吃得是蛋还是饭。

感慨于此,就写下了这篇文章,总结下自己在使用这两种语言的过程中的心得,加深自己对于这两种语言底层原理的理解,分享自己的心得体会。
玩蛇即指写 Python,喝咖啡即指写 Java,至于为什么,看完这篇文章就会明白的。

因为内容较长,可能又得分篇进行描述了。
这一篇会主要记录两种语言的发展历程、Hello World、语法特点、执行效率以及 Python 的执行机制。
下一篇会记录 Java 的执行机制与 Java 虚拟机、两种语言的并发、内存回收机制。

其中,Python 主要以 Python 3.8.2 为例,Java 以 Java SE 11 为主,同时穿插 JVM 语言 Kotlin 与 Groovy。

发展历程

Python

最早的 Python 由 Guido van Rossum 于 1980 年代后期开始研究,1991 年发布了 0.9.0 版本。

Python 的命名主要来自于英国喜剧团体 Monty Python,而 Python 的一词的原意是蟒蛇,这也是为啥 Python 的 Logo 是一条蛇。

我们比较熟悉的版本为 Python 2 和 Python 3,分别于 2000 年和 2008 年发布。其中 Python 3 并不向后兼容,即 Python 3 并不兼容 Python 2 的代码,这一点与 Java 有很大的区别。

Java

最早的 Java 由 James Gosling、Mike Sheridan 和 Patrick Naughton 于 1991 年 6 月 开始研究,设计之初的语法便参考了 C/C++ 的语法。

Java 的命名历经 oak(橡树),Green 和现在的 Java(Java 是一种印度尼西亚生产的咖啡豆,这也是为什么 Java 的 logo 是一杯咖啡)。

Java 1.0 于 1996 年发布。目前常用的版本为 Java SE 8,Java SE 11 和 Java SE 17。

与 Python 不同的是,Java 的向后兼容性非常好,多数的 JRE 都具备向后兼容的能力,但也因此,Java 背上了沉重的历史包袱。

Hello World

对于 Hello World 而言,Python 几乎是所有编程语言中最简洁的,仅需要一行代码:

print("hello world!")

而相对而言,Java 就显得略显繁琐:

public class demo{public static void main(String[] args){System.out.println("hello world!");}
}

当然,考虑到 JVM 语言并不是只有 Java,有不少 JVM 语言都对 Java 进行了优化。

例如 Kotlin:

fun main(args: Array<String>){println("hello world!")
}

相对于 Java,Kotlin 的语法显得格外简洁。Kotlin 可以省略行尾的分号,同时 main 函数可以单独出现,而不必在某个类中。

例如 Groovy:

println("Hello World")

Groovy 的 Hello world 相比前两者的简洁程度则更进一步,不仅不需要存在于某个类中,连 main 函数都可省略。

语法特点

说编程语言的语法特点就不得不说编程范式。

Robert Floyd 在 1979 年图灵奖的颁奖演说中使用了编程范式一词。简单来说,**编程范式是程序员看待程序应该具有的观点,代表了程序设计者认为程序应该如何被构建和执行的看法。**常见的编程范式有:命令式、声明式、过程式、面向对象、函数式、泛型编程等。

  • 命令式:用语句更改程序的状态的编程范式。

  • 声明式:它指定程序应该做什么,而不具体说明怎么做。例如 SQL 和正则。

  • 面向对象:关键词为 封装 抽象 继承 多态

一些编程语言是专门为某种特定范式设计的,例如 C 语言是过程式编程语言,Java 是较纯粹的面向对象编程语言。但编程语言和编程范式的关系并不一一对应,Python,Groovy 都支持面向对象和一定程度上的函数式编程。

前面说到,Java 在设计之初,其语法很大程度上收到 C/C++ 的影响。但与 C++ 不同的是,Java 中除了 8 种基本类型之外,全部是基于对象的。Java 代码都是写在类里面的。因此说 Java 是比较纯粹的面向对象编程语言。然而 Java 8 开始引入了 Stream,提供了函数式编程的能力。

而对于 Python,Python 既支持过程式编程,也支持面向对象。因此相对而言 Python 的编程语法较为自由。


由于 Java 与 Python 的语法区别,两者在真正完成编程工作过程中的感觉也是不一样。

有一种说法是,写 Java 是自顶向下的,而写 Python 是自底向上的。

这是可能是因为 Java 的面向对象特性,使得在编程过程中会不自觉的先定义父类,然后继承父类,根据多态来定义子类应有的属性和方法。或者是先定义接口,再根据接口去实现对应的函数。这个过程会使得程序员在编程过程中不自觉的自顶向下的进行思考,会在拿到需求后先进行抽象和设计,再逐步实现。

而 Python 则有所不同。由于 Python 是一门多范式的编程语言,对过程式编程的支持使得用 Python 实现一个功能变得很方便。

同时 Python 本身内置了大量方便简洁的 API,如序列化用的 json,文件目录处理 os 等,这些 API 让程序员面对一些用 Java 来做可能需要创建好几个类才能实现的功能简单调用一下 API 就能实现,这样也使得程序员不会更多的考虑如何对这个过程进行抽象和设计,而是单纯从效率出发,尽快的实现功能。但却使得后续的维护和扩展成本变得更高。

因此对比两种语言,我个人觉得使用他们的编程成本的曲线可能是这样的:

在功能复杂度较低的时候,Python 的开发成本更低,可以更快的实现所需功能。但当功能复杂度较高时,Java 的优势边开始显现。在一个设计规划良好的 Java 项目中,功能复杂度的增加对于 Java 开发成本的增加会比 Python 低得多。

执行效率

在Python、Java 与 C 的性能对比一文中, Java、Python 与 C 在相同规模的矩阵运算任务的耗时如下:

显然我们能够看出,Python 的运行效率可以说是完败于 Java 和 C 的。
这是因为 Python 的执行过程是需要逐行解释的,同时作为动态语言,Python 需要在运行过程中确定对象的类型。这也大大降低了 Python 的运行效率。

然而,观察上图更有意思的是 Java 的执行效率竟然比未经 GCC 优化的运行效率要高得多!这就牵扯到 Java 的执行机制了。

与 C 相同,Java 也会有编译的过程,不过该过程只是将源文件(后缀为.java)通过 Java 编译器编译成二进制的字节码文件(后缀为.class),将字节码文件放入 JVM 中执行。真正使得 Java 运行效率大增的是 JIT(Just-in-time compilation,即时编译)技术,该技术通过在运行过程中将字节码编译成本地机器代码来提高 Java 程序的性能。JVM 可以直接调用编译后的代码,而不需要耗费处理器时间和内存来解释代码。

执行机制

刚才已经说过,Python 与 Java 的性能差异来自其执行机制的不同,那么两者的执行机制有什么区别呢?

Python 的执行机制比较简单,先来看看 Python 的。

Python 的执行机制与 PVM(Python 虚拟机)

CPython 与其他 Python 实现

在解释 Python 的执行机制之前,首先得定义清楚我们说的 Python 是啥。

我们经常会说,Python 是一门解释性语言,仿佛是说运行 Python 代码的过程中是 Python 解释器在逐步解释每一行 Python 源码,而不存在编译的过程。
然而事实是这样的吗?

并不是这样的。

Python 是一种编程语言,但这只是说明了其语法规定。但对于 Python 的实现,是有很多类型的。

我们一般用的 Python,默认都是指 CPython,即用 C 语言写成的 Python。
但是除了 CPython 以外,还有 Jython、IronPython 等等。
Jyhton 会将 Python 代码编译成 Java 字节码,运行在 JVM 上;
IronPython 会编译成 CLR 代码,运行在 .NET 上。
由于要运行在特定的虚拟机中,这些非 CPython 的实现都存在编译过程。

而回到广泛使用的 Python 版本 CPython,它同样存在一个编译过程,只不过由于 CPython 在设计时主要考虑的就是编译过程尽可能的快,因此这个过程常常短到被忽略。(但也同样由于这个编译过程较短,带来的优化较少,成为 Python 执行效率较低的原因之一)

所以说 Python 是一门解释性语言 这个说法本身就不对,准确的说法是,Python 是一门动态语言。(动态语言是指存在运行过程中的类型推断,而不是在运行前就已经确定对象的类型)

CPython 的编译

接下来针对 CPython 的编译过程进行详细讲解。

Python 源代码是以 .py 结尾的文本文件,在 Python 3 中默认是以 utf-8 格式编码存储的。

在 Python 执行前,Python 解释器会将 .py 文件编译为 .pyc 格式的字节码文件,然后交由 Python 虚拟机执行。

(有没有感觉这个过程和 JVM 的执行很类似?)

为了能够更加方便的理解 Python 的执行过程,我参考了博主 smartkeyerror 写的 Pyton 虚拟机一文,通过一段代码详细的展示这个过程:

首先我们定义一个简单的函数:

def func():for i in range(3):print(f"output: {i}")

如果将这段代码以文本形式存储,然后调用 Python 的内建函数 compile 进行编译,会得到这段代码对应的 Code Object 对象:

snippet = """
def func():for i in range(3):print(f"output: {i}")
"""result = compile(snippet, "", "exec")
print(type(result))
# 输出为 <class 'code'>
print(result)
# 输出为 <code object <module> at 0x000001D95B1EFC90, file "", line 2>

这里我们得到的 reslut 就是 Code Object 对象,在该对象中保存着源代码所对应的字节。

查看 CPython 的源代码可以在 cpython/Include/code.h 中找到 PyCodeObject 结构体,即 Code Object 对象:

/* Bytecode object */
typedef struct {PyObject_HEAD               /* Python定长对象头 */PyObject *co_code;          /* 指令操作码,即字节码 */PyObject *co_consts;        /* 常量列表 */PyObject *co_names;         /* 名称列表(不一定是变量,也可能是函数名称、类名称等) */PyObject *co_filename;      /* 源码文件名称 */...                         /* 省略若干字段 */
} PyCodeObject;

字段 co_code 即为 Python 编译后字节码,其它字段在此处可暂时忽略。字节码的格式为人类不可阅读格式,其形式通常是这样的:

print(result.co_code)
# 输出为 b'd\x00d\x01\x84\x00Z\x00d\x02S\x00'

通过调用 Python 字节码反编译器 dis,可以反编译 result.co_code 字节码得到助记符:

import dis
dis.dis(result.co_code)

这就是定义函数 fun 对应的 Python 字节码。可以看出来这已经非常类似于汇编语言的指令了,均由操作指令和操作数所组成。

          0 LOAD_CONST               0 (0)2 LOAD_CONST               1 (1)4 MAKE_FUNCTION            06 STORE_NAME               0 (0)8 LOAD_CONST               2 (2)10 RETURN_VALUE

以上的助记符不难看出是关于如何定义函数的。如果想看函数功能代码对应的助记符,可以直接反编译函数本身:

dis.dis(func)

得到结果如下:

  2           0 LOAD_GLOBAL              0 (range)2 LOAD_CONST               1 (3)4 CALL_FUNCTION            16 GET_ITER>>    8 FOR_ITER                18 (to 28)10 STORE_FAST               0 (i)3          12 LOAD_GLOBAL              1 (print)14 LOAD_CONST               2 ('output: ')16 LOAD_FAST                0 (i)18 FORMAT_VALUE             020 BUILD_STRING             222 CALL_FUNCTION            124 POP_TOP26 JUMP_ABSOLUTE            8>>   28 LOAD_CONST               0 (None)30 RETURN_VALUE

看到这里我们就能明白 Python 的编译过程是怎么回事了:

Python 在编译某段源码时,并不会直接返回字节码,而是返回一个 Code Object 对象,字节码则保存在该对象的 co_code 字段中。由于字节码是一个二进制字节序列,无法直接进行阅读,所以需要通过”反汇编器”(dis 模块)将字节码转换成人类可读的助记符。助记符的形式和汇编语言非常类似,均由操作指令+操作数所组成。

Python 字节码的执行

指令执行的源码均位于 cpython/Python/ceval.c 中,入口函数有两个,一个是 PyEval_EvalCode ,另一个则是 PyEval_EvalCodeEx ,最终的实际调用函数为 _PyEval_EvalCodeWithName,所以我们只需要关注该函数即可。

_PyEval_EvalCodeWithName 函数的主要作用为进行函数调用的例常检查,例如校验函数参数的个数、类型,校验关键字参数等。除此之外,该函数将会初始化栈帧对象并将其交给 PyEval_EvalFrame 函数进行处理,最终由 _PyEval_EvalFrameDefault 函数真正的运行指令。

_PyEval_EvalFrameDefault 函数定义超过了 3K 行,绝大部分的逻辑其实都是 switch-case ,即根据指令类型执行相应的逻辑。

for (;;) {switch (opcode) {case TARGET(LOAD_CONST): {      /* 加载常量 */...}case TARGET(ROT_TWO): {         /* 交换两个变量 */...}case TARGET(FORMAT_VALUE):{     /* 格式化字符串 */...}

可以看到 TARGET() 调用中的参数其实就是 dis 方法返回的助记符,当我们在分析助记符的具体实现逻辑时,可以在该文件中找到对应的 C 实现方法。

如果对 Python 字节码指令感兴趣,可以在 Python 官方文档中关于虚拟机字节码指令的部分 查阅。

Python 执行机制小结

根据上述分析可以看出,Python 其实也是存在虚拟机的,同时也存在编译的过程。Python 的虚拟机是由 C 语言实现的,但相对于 Java 虚拟机,Python 的虚拟机比较简单,编译过程中对性能的优化也比较弱,这一点在后续 Java 执行机制的分析中可以更清楚的感受到。


如有帮助,欢迎点赞/转载~
(听说给文章点赞的人代码bug特别少

深度对比 Python 与 Java 的区别(一)相关推荐

  1. python和java的区别-Python和Java的区别有哪些?如何选择?

    原标题:Python和Java的区别有哪些?如何选择? 随着人工智能AI的发展越来越快速,很多人对于Python开发技术也是越来越重视,也正因如此,不少人拿着Java和Python进行比较,认为Pyt ...

  2. python和java的区别-python与java区别

    Python和Java都是很火的编程语言,对于想学习编程的人员来说,常常被这个问题所困扰:我是该学Python还是Java呢?想要解决这个问题,还需结合自身实际情况和两种语言的特点进行分析. 以下是P ...

  3. java和python的比较-如何对比Python和Java,只需三分钟告诉你!

    原标题:如何对比Python和Java,只需三分钟告诉你! Java和Python两门语言都是目前非常热门的语言,可谓北乔峰南慕容,二者不分上下,棋逢对手.但是对于初学者来说,非常困惑,因为时间和精力 ...

  4. java和python的web自动化有什么区别-三分钟看懂Python和Java的区别

    随着人工智能的火爆,Python和Java一直在各种流行编程语言中名列前茅.其实Java和Python有些相似,因为很多编程语言之间是互通的.Java现在还是第一,不知道Python未来会不会超越Ja ...

  5. python和java一样吗-三分钟看懂Python和Java的区别

    随着人工智能的火爆,Python和Java一直在各种流行编程语言中名列前茅.其实Java和Python有些相似,因为很多编程语言之间是互通的.Java现在还是第一,不知道Python未来会不会超越Ja ...

  6. python和java一样吗-python和java的区别,看了这个就会区分了!

    翻看日历,2019年结束也就剩下3个月的时间了!时光飞逝,伴随着互联网人工智能时代的发展,人类一直在预测,2020年什么技术会火?其中,人工智能编程成了大家讨论的热点,Python.Java一直被人们 ...

  7. python和java的区别-三分钟看懂Python和Java的区别

    随着人工智能的火爆,Python和Java一直在各种流行编程语言中名列前茅.其实Java和Python有些相似,因为很多编程语言之间是互通的.Java现在还是第一,不知道Python未来会不会超越Ja ...

  8. python和java的区别-python和java的区别,看了这个就会区分了!

    翻看日历,2019年结束也就剩下3个月的时间了!时光飞逝,伴随着互联网人工智能时代的发展,人类一直在预测,2020年什么技术会火?其中,人工智能编程成了大家讨论的热点,Python.Java一直被人们 ...

  9. java与python难度对比_Python和Java的区别,看完这篇文章你就清楚啦

    众所周知,在数不清的编程语言中Java自诞生之日起长盛不衰,可谓是神话般的存在.随着人工智能时代的到来,Python迅速席卷全球,作为当下最热门的编程语言,因其简单实用且应用场景广泛备受青睐. 一个是 ...

最新文章

  1. OpenCV读写视频文件解析
  2. ECLIPSE3.2安装 + 汉化 + 配置SWT DESIGNER6.2.0
  3. ACM PKU1703 Find them, Catch them
  4. mysql 多列合并为一列_多列数据合并一列,还在用数据透视就out了,用=号只要三步完成...
  5. React开发(137):ant design学习指南之form中日期时间处理format时间处理
  6. 前端学习(1774):前端调试之local storage原理和查看
  7. JAVA.SQL.SQLNONTRANSIENTCONNECTIONEXCEPTION: COULD NOT CREATE CONNECTION TO DATABASE SERVER. ATTEMPT
  8. TCP / IP攻击:ARP缓存中毒的基本原理、TCP序列号预测和TCP重置攻击
  9. ipython notebook使用教程
  10. C#利用反射,遍历获得一个类的所有属性名,以及该类的实例的所有属性的值
  11. 如何删除MySQL服务
  12. Misc-Xp0int(数据包分析)
  13. Java面向对象OOP思想概述
  14. excel如何把顺序倒过来_excel倒序怎么操作
  15. cd linux自带系统安装,大神示范win7系统将CDLinux装入硬盘的法子
  16. SVT和ULVTcell比较
  17. 盘点2022年爆火的小程序游戏
  18. excel和python建模_如何用Python提高办公(Excel)效率?
  19. Ugurgallen只是使用ps的简单拼贴技术,却刺痛42万人的心!
  20. 闪讯客户端 linux,Linux操做系统下链接闪讯的方法(支持有线与无线)

热门文章

  1. linux电脑外放没声音,你好,就是想问一下我的笔记本电脑为什么插上耳机没有声音,外放有声音,声卡驱动我也重新装了,还是不行!...
  2. Blog3 无监督深度关键短语生成——关键代码分析1
  3. 一套代码实现1对1 、1对N在线课堂与低延迟大班课
  4. 网易2019年Q3财报净利47.26亿元 从财报看网易的长期主义
  5. 车路协同V2X调研分析
  6. 面试积累(简单的工厂模式)
  7. 机器人 c语言编程题,机器人机器人小高试题模拟题
  8. 如何屏蔽 iOS 16 软件自动更新,去除更新通知和标记
  9. 写给学生看的系统分析与验证笔记(十五)——计算树逻辑(Computation tree logic,CTL)
  10. You may have an infinite update loop in a component render function问题解决