java和python对⽐
⼀:解释性和编译型
梳理
编译型:源代码经过编译直接变为⼆进制的机器语⾔,每次都可以直接重新运⾏不需要翻译。典型的就是c、go。
解释性:python是解释型,python也有⼀个编译过程,它的编译是把源⽂件编译为.pyc⽂件,为了加快python的加载过程,⽆法提⾼python运⾏过程。
java的解释是把源⽂件编译为.class⽂件,字节码⽂件,有了jvm⼀次编译处处执⾏,是解释和编译的混合体。
Oracle JDK提供的Hotspot JVM,都提供了⼀个JIT(Just-In-Time)⼯具也就是通常说的动态编译器,JIT能够在运⾏时将热点代码编译成机器码,这种情况下部分热点代码就属于编译执⾏的;不过需要强调的是,java并不是编译机制⽽是解释机制。
源代码经过编译变为中间代码(字节码⽂件),⾮⼆进制代码,然后将字节码放在VM上运⾏,达到跨平台的⽬的。
解释器将中间代码转为⼆进制代码。
强类型,指数据类型定了之后不能改变,java也是强类型。
弱类型,js就是弱类型。
java和python都是边解释边执⾏,但是解释之前都先进⾏了编译⼯作,编译为vm能看懂的字节码,vm解释的是字节码,⾮源码和机器语⾔。
但是为什么python⽐java慢了⼀个级别?
主要原因是python是动态语⾔,java是静态语⾔。
静态语⾔:变量声明的时候要声明变量类型,这样编译器在程序运⾏时知道变量的类型,提前按数据类型规划好了内存区域,起来范围⼩⼗分的快捷。
动态语⾔:在程序运⾏时解释器只知道变量是⼀个对象,⾄于具体是什么类型解释器并不知道,所以每次程序运⾏的时候都要判断变量的类型,然后再去此变量。
java对于相同类型,编译像预演⼀样,在现场直播时安排连续的⼀块⼩内存存放,寻址速度快,还有JIT的编译优化等。
python则是程序分配的全内存寻址,⽐java肯定慢了。
还有像容器类型,java中的数组变量类型必须⼀致;但是python中list⽤的多,变量类型任意。
java中对变量类型还进⾏了细分,java中分直接传递和引⽤传递,python全部都是引⽤传递。(直接传递,拷贝数据;引⽤传递,传内存地址。)
因为动态语⾔多了⼀个类型判断的过程,因此python⽐java慢了⼀个级别。
动态语⾔是简单,但是解释器在背后做的事情⽐静态语⾔的解释器在背后做了更多的事情。
python两个概念,PyCodeObject和pyc⽂件
在硬盘上看到的pyc⾃然不必多说,⽽其实PyCodeObject则是Python编译器真正编译成的结果。
当python程序运⾏时,编译的结果保存在位于内存中的PyCodeObject中,当Python程序运⾏结束时,Python解释器则将内存中的PyCodeObject写回到pyc⽂件中,前提有写权限。
当python程序第⼆次运⾏时,⾸先程序会在硬盘中寻pyc⽂件,如果到对⽐修改时间,改动了就重新编译,没改动则直接载⼊,否则就重复上⾯的过程。
所以我们应该这样来定位PyCodeObject和pyc⽂件,我们说pyc⽂件其实是PyCodeObject的⼀种持久化保存⽅式。
test.py如下
def test():
print("Hello World")
if __name=="__main__":
test() 
此时python test.py发现硬盘中根本没有pyc⽂件
但是如果把函数定义放到另⼀个a.py⽂件中,在test.py中from a import test
此时再python test.py就会发现有了pyc⽂件。这说明了什么?
说明了pyc⽂件的⽬的是为了重⽤。
python解释器认为只有import的模块,才是要被重⽤的模块。对于test.py⽂件来讲,解释器不认为他是需要被重⽤的模块,因为他会被经常的改动,把它持久化是画蛇添⾜的,因为每次都要持久化为pyc⽂件,因此python只会把可重⽤的模块持久化为pyc⽂件。
速度
字节码并不能加快程序的运⾏速度,只是加快了代码的加载速度。
源代码都会被编译为字节码,java和Python都有这⼀步,当python运⾏主⽂件的时候,会将⽤到的其他模块全部编译为⼆进制的pyc⽂件来加快第⼆次运⾏程序时加载模块的速度,省去了源代码转为字节码的过程,解释器直接在pvm中拿到pyc⽂件直接执⾏。
当创建pyc⽂件的时候会和模块⽂件的最后⼀次修改时间进⾏映射,⼀旦第⼆次运⾏时会⽐较修改时间,如果修改时间没变就直接拿pyc⽂件执⾏,如果改变了就重新编译。
如果没有创建⽂件的权限,那么pyc⽂件是在内存存在的,每次运⾏pyc⽂件都是在内存重新编译⽣成,⽆法加快程序加载速度。
编译
java的编译就像预演⼀样,会把你所有的错误出来,例如结果是none,然后你调⽤了⼀个⽅法,编译绝对⽆法通过,更加⽆法执⾏。
python是没有预演,只有现场直播,代码只有执⾏到错误位置才会报错,如果没有执⾏到是不会报错的,根据表演者(程序员)的⽔平⾼低,经验少的就会在直播时出很多错误,经验丰富就会犯的错误少,同java⼀样对none对象调⽤⽅法,只有运⾏起来才会发现错误。
设计初衷
python将开发者视为合格的开发⼈员,会⾃觉的遵守规范,当然你可以不遵守,那只能说明你“不合”,并不是语⾔本⾝有问题,例如mixin机制,常量等都没有硬语法约束,⽽是规范。
java就像⼀个严格的监督者,你必须按照它要求的来,不按要求来就是错误,会在语法层⾯上约束的死死的。
java的设计严谨,总感觉有些过度设计,就像2020疫情中诞⽣的⼀个概念“去过度化”。
java没有多继承,⽤了接⼝的概念。实现逻辑上is...a与a的逻辑区分。
python虽然⽀持多继承,但是不推崇多继承,⽽是推崇鸭⼦类型,设计者把类设计的相似,长的像鸭⼦那么就是鸭⼦。
这样逻辑上虽然是同⼀类鸭⼦,但是代码层⾯上类之间是毫⽆关系的,完全解耦。
python崇尚鸭⼦类型和魔法⽅法(还有namespace),让python天⽣就是多态,
java的多态要建⽴继承关系,⼦类重写⽗类;定义时放⽗类/接⼝类型,调⽤时传⼊⼦类/实现类对象,刻意去多态,
python内置的协议就是魔法⽅法,不同的魔法⽅法分别属于不同的⼤类
python没有接⼝的概念,尽管python⽤abc模块模仿了抽象类,但我⼏乎没怎么⽤过。
魔法⽅法就像是彩蛋⼀样,有各种被动的触发⽅式,任何⼀个类都可以将魔法⽅法放到⾥⾯来增强类的表达。
python⼀切皆对象:元类type继承object类,元类type实例化了object类对象,听上去和现实是⽭盾的,好像是⼀个闭环,但是在代码中并不⽭盾,也是可以实现的。
⽭盾是因为和现实世界映射对⽐起来⽭盾:type继承了⽗类object,然后type⼜⾃⼰实例化了⾃⼰,然后⼜实例化了⽼爹,这什么玩意?
继承爹,⼜实例化爹在启动阶段并不⽭盾,继承了静态的代码⼜没有调⽤爹的⽅法,开个后门让他过去
type类没⽤爹的⽅法,但是其他object类可能会使⽤object的⽅法,那没问题,只要其他对象在obejct实例化之后创建就可以了。
type继承object,object是对象,type也是对象;⽽type创建了所有的对象,那么⼀切都是对象。
type对象都是type⾃⼰创建的,⽤了⼀个指针⽽已。
python⼀切皆对象,1在java是基本数据类型,存放栈中,⽽pyhton⼀切皆对象,a=1背后是a=int(1),int是内置类,⽤的⼩写,1也是⼀个对象,在堆⾥⾯分配内存
堆空间⽐栈空间⼤的多,⼆者都去1,当然java的快。
总结
java和python程序速度差⼀个量级是因为语⾔本⾝的特性,静态和动态,与pyc⽂件毫⽆关系。
虽然速度差了⼀个量级,但是⼤多应⽤是⽆感知,0.1秒和0.01秒感觉都是过了1秒,⼜不是作购物⽹
站,促销时遇到⼤规模并发,每⼀门语⾔有他擅长的领域,如果⼀门语⾔擅长所有的领域,那么早天下统⼀了。
python是顺序执⾏代码的和if __name__=="__main__"⽆关。java调用python模型
⼀份程序为了区分主动执⾏还是被调⽤,Python引⼊了变量__name__,当⽂件是被调⽤时,__name__的值为模块名,当⽂件被执⾏时,__name__为'__main__'
主动执⾏的叫脚本,被引⼊使⽤的叫模块。
1 #/usr/bin/env/ python            #(1) 起始⾏
2 #"this is a test module"          #(2) 模块⽂档(⽂档字符串)
3 import sys
4 import os              #(3) 模块导⼊
5
6 debug = True              #(4) (全局)变量定义
7 class FooClass (object):
8    'foo class'
9    pass                          #(5) 类定义(若有)
10 def main():
11    'test function'
12    foo = FooClass()
13    if debug:
14        print 'ran test()'        #(6) 函数定义(若有)
15 if __name__ == '__main__':
16    main()
若是⽂件主动执⾏了,则最好写成跟上⾯的例⼦⼀样,main之前不要有可执⾏代码,这样做到程序从main()开始,流程逻辑性强
若是⽂件作为模块被调⽤,则可以不⽤写main(),从上⽽下顺序执⾏。
其实Python是否保存成pyc⽂件和我们在设计缓存系统时是⼀样的,我们可以仔细想想,到底什么是值得扔在缓存⾥的,什么是不值得扔在缓存⾥的。
在跑⼀个耗时的Python脚本时,我们如何能够稍微压榨⼀些程序的运⾏时间,就是将模块从主模块分开。(虽然往往这都不是瓶颈)
在设计⼀个软件系统时,重⽤和⾮重⽤的东西是不是也应该分开来对待,这是软件设计原则的重要部分。
在设计缓存系统(或者其他系统)时,我们如何来避免程序的过期,这也是Python的解释器也为我们提供了⼀个特别常见⽽且有效的解决⽅案。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。