Python语⾔特性:编译+解释、动态类型语⾔、动态语⾔、运⾏
速度
1.1 定义
1.2 Python 属于编译型还是解释型?
1.3 收获
2.1 定义
2.2 ⽐较
3.1 定义
3.2 Python 动态语⾔的体现
3.3 __slots__()
1. 解释性语⾔和编译性语⾔
1.1 定义
计算机是不能够识别⾼级语⾔的,所以当我们运⾏⼀个⾼级语⾔程序的时候,就需要⼀个“翻译机”来从事把⾼级语⾔转变成计算机能读懂的机器语⾔的过程。这个过程分成两类,⼀类是编译,⼀类是解释。
解释型语⾔
程序执⾏前不需要先进⾏编译,⽽是在执⾏时才通过解释器对代码进⾏翻译,翻译⼀句然后执⾏⼀句,直⾄结束。
编译型语⾔
编译性语⾔写的程序在被执⾏之前,需要⼀个专门的编译过程,把程序编译成为机器语⾔(⼆进制代码)的⽂件,⽐如 exe ⽂件,此后再运⾏时就不⽤重新翻译了,直接使⽤编译后的结果⽂件(exe ⽂件)来运⾏就⾏。
因为编译型语⾔在程序运⾏之前就已经对程序做出了“翻译”,所以在运⾏时就少掉了“翻译”的过程,因此效率⽐较⾼。但是我们也不能⼀概⽽论,⼀些解释型语⾔也可以通过解释器的优化来在对程序做出翻译时对整个程序做出优化,从⽽在效率上超过编译型语⾔。
此外,随着 Java 等基于虚拟机的语⾔的兴起,我们⼜不能把语⾔纯粹地分成解释型和编译型这两种。⽤ Java 来举例,Java ⾸先是通过编译器编译成字节码⽂件(不是⼆进制码),然后在运⾏时通过解释器(JVM)给解释成机器代码才能在各个平台执⾏,这同时也是 Java 跨平台的原因。所以我们说 Java 是⼀种先编译后解释的语⾔。
总结:将由⾼级语⾔编写的程序⽂件转换为可执⾏⽂件(⼆进制的)有两种⽅式,编译和解释,编译是在程序运⾏前,已经将程序全部转换成⼆进制码,⽽解释是在程序执⾏的时候,边翻译边执⾏。
编译型语⾔:执⾏效率⾼;依靠编译器,因此跨平台性差些。
解释型语⾔:执⾏效率低;依靠解释器,因此跨平台性好。
1.2 Python 属于编译型还是解释型?
其实 Python 和 Java ⼀样,也是⼀门基于虚拟机的语⾔,我们先来从表⾯上简单地了解⼀下 Python 程序的运⾏过程。
当我们在命令⾏中输⼊ python hello.py 时,其实是激活了 Python 的“解释器”,告诉“解释器”要开始⼯作了。可是在“解释”之前,其实执⾏的第⼀项⼯作和 Java ⼀样,是编译。
熟悉 Java 的同学可以想⼀下我们在命令⾏中如何执⾏⼀个 Java 的程序:
javac hello.java(编译的过程)
java hello(解释的过程
只是我们在⽤ Eclipse 等 IDE 时,将这两步给融合成了⼀步⽽已。其实 Python 也⼀样,当我们执⾏ python hello.py 时,他也⼀样执⾏了这么⼀个过程,所以我们应该这样来描述 Python,Python 是⼀门先编译后解释的语⾔。
1.2.1 简述 Python 的运⾏过程
在说这个问题之前,我们先来说两个概念,PyCodeObject 和 pyc ⽂件。
当 Python 程序⾸次运⾏时,编译的结果保存在位于内存中的 PyCodeObject 中。当 Python 程序运⾏结束时,Python 解释器则将PyCodeObject 写回到硬盘中的 pyc ⽂件中。
当 Python 程序第⼆次运⾏时,⾸先程序会在硬盘中寻 pyc ⽂件,如果到则直接载⼊,否则就重复上⾯的过程。
所以我们应该这样来定位 PyCodeObject 和 pyc ⽂件,我们说 pyc ⽂件其实是 PyCodeObject 的⼀种持久化保存⽅式。
也就是说保存 pyc ⽂件是为了下次再次使⽤该脚本时避免重复编译,以此来节省时间。因此,只执⾏⼀次的脚本,就没必要保存其编译结果pyc ⽂件,这样只是浪费空间。下⾯举例解释。
⽰例 1:执⾏不含“import”关键字的代码
a.py 代码如下:
print("hello world")
执⾏ a.py:
E:\test>python a.py
hello world
此时我们可以发现,在执⾏所在⽬录下并没有产⽣ pyc ⽂件,仍只有 a.py。
⽰例 2:执⾏含“import”关键字的代码
新增 b.py,代码如下:
import a
执⾏ b.py:
E:\test>python b.py
hello world
此时我们可以发现,pyc ⽂件产⽣了。
1.2.2 pyc ⽂件的⽬的是重⽤
编译型语⾔的优点在于,我们可以在程序运⾏时不⽤解释,⽽直接利⽤已经“翻译”过的⽂件。也就是说,我们之所以要把 py ⽂件编译成 pyc ⽂件,最⼤的优点在于我们在运⾏程序时,不需要重新对该模块进⾏重新的解释。
所以,我们需要编译成 pyc ⽂件的应该是那些可以重⽤的模块,这与我们在设计软件时是⼀样的⽬的。所以 Python 解释器认为:只有import 进来的模块,才是需要被重⽤的模块。
这个时候也许有⼈会说,不对啊!你的这个问题没有被解释通啊,我的 test.py 不是也需要运⾏么,虽然不是⼀个模块,但是以后我每次运⾏也可以节省时间啊!OK,我们从实际情况出发,思考下我们在什么时候才可能运⾏ python xxx.py:
1. 执⾏测试。
2. 开启⼀个 web 进程。
3. 执⾏⼀个程序脚本。
第⼀种情况(执⾏测试),这时哪怕所有的⽂件都没有 pyc ⽂件都是⽆所谓的。
第⼆种情况(开启⼀个 web 进程),我们试想⼀个 web 程序通常这样执⾏:
或
然后这个程序就类似于⼀个守护进程⼀样⼀直监听着 8181/9002 端⼝,⽽⼀旦中断,只可能是程序被杀死,或者其他的意外情况,那么你需要恢复要做的是把整个的 Web 服务重启。既然⼀直监听着,把 PyCodeObject ⼀直放在内存中就⾜够了,完全没必要持久化到硬盘上。
所以我们可以这样理解 Python 解释器的意图,Python 解释器只把我们可能重⽤到的模块持久化成 pyc ⽂件。
1.2.3 pyc 的过期时间
说完了 pyc ⽂件,可能有⼈会想到,每次 Python 的解释器都把模块给持久化成了 pyc ⽂件,那么当我的模块发⽣了改变的时候,是不是都要⼿动地把以前的 pyc ⽂件 remove 掉呢?
当然 Python 的设计者是不会犯这么⽩痴的错误的。⽽这个过程其实就取决于 PyCodeObject 是如何写⼊ pyc ⽂件中的。
我们来看⼀下 import 过程的源码吧:
这段代码⽐较长,我们只看标注的代码,其实它在写⼊ pyc ⽂件的时候,写了⼀个 Long 型变量,变量的内容则是⽂件的最近修改⽇期,同理,我们再看下加载 pyc 的代码:
不⽤仔细看代码,我们可以很清楚地看到原理,其实每次在加载 pyc 之前都会先检查⼀下 py ⽂件和 pyc ⽂件保存的最后修改⽇期,如果不⼀致则重新⽣成⼀份 pyc ⽂件。
1.3 收获
其实了解 Python 程序的执⾏过程对于⼤部分程序员(包括 Python 程序员)来说意义都是不⼤的,那么真正有意义的是,我们可以从Python 的解释器的做法上学到什么,我认为有这样的⼏点:
1. 其实 Python 是否保存成 pyc ⽂件和我们在设计缓存系统时是⼀样的,我们可以仔细想想,到底什么是值得扔在缓存⾥的,什么是不值
得扔在缓存⾥的。
2. 在跑⼀个耗时的 Python 脚本时,我们如何能够稍微压榨⼀些程序的运⾏时间,就是将模块从主模块分开(虽然往往这都不是瓶颈)。
3. 在设计⼀个软件系统时,重⽤和⾮重⽤的东西是不是也应该分开来对待,这是软件设计原则的重要部分。
4. 在设计缓存系统(或者其他系统)时,我们如何来避免程序的过期,其实 Python 的解释器也为我们提供了⼀个特别常见⽽且有效的解
决⽅案。
2. 动态类型语⾔
2.1 定义
动态类型语⾔
所谓动态类型语⾔,就是(变量、属性、⽅法以及⽅法的返回值)类型的检查(确定)是在运⾏时才做。
即编译时与类型⽆关。⼀般在变量使⽤之前不需要声明变量类型,⽽变量的类型通常是由被赋的值的类型决定。如 Php、Python 和Ruby。
静态类型语⾔
与动态类型语⾔正好相反,在编译时便需要确定类型的语⾔。即写程序时需要明确声明变量类型。如 C/C++、Java、C# 等。
对于动态语⾔与静态语⾔的区分,套⽤⼀句流⾏的话就是:Static typing when possible, dynamic typing when needed。
强类型语⾔
强制数据类型定义的语⾔。也就是说,⼀旦⼀个变量被指定了某个数据类型,如果不经过强制转换,那么它就永远是这个数据类型了。因此强类型定义语⾔是类型安全的语⾔。
弱类型语⾔
数据类型可以被忽略的语⾔。它与强类型定义语⾔相反, ⼀个变量可以赋不同数据类型的值。
强类型定义语⾔在速度上可能略逊⾊于弱类型定义语⾔,但是强类型定义语⾔带来的严谨性能够有效的避免许多错误。
Python 属于动态类型语⾔和弱类型语⾔。
2.2 ⽐较
严格意义上,强类型与静态类型不是⼀回事,同理弱类型和动态类型。
强类型是指某门语⾔检查两种类型是否兼容,如果不兼容就抛出⼀个错误或强制类型转换,尽管这个说法并不是很严格。
静态类型强迫在类型结构的基础上执⾏多态。判断是否是⼀只鸭⼦的依据,是其基因蓝图(静态)还是因其叫声和⾛路的姿态像⼀只鸭⼦(动态)。
静态类型语⾔:
优点:在于其结构⾮常规范,突出显⽰代码以便于调试,⽅便类型安全。
缺点:需要写更多的类型相关代码(如声明变量),不便阅读(特别是当你看别⼈代码时,会连变量定义也看吗?想必不会,看结构,看⽅法的含义想必才是本质)。
动态类型语⾔:
优点:在于⽅便阅读,不需要写⾮常多的类型相关代码。
缺点:⾃然是不便调试,命名不规范时会造成读不懂,不利于理解等。
在强类型、静态类型语⾔的⽀持者,与动态类型、⾃由形式的⽀持者之间,经常发⽣争执:
前者主张,在编译的时候就可以较早发现错误,⽽且还可增进运⾏时期的性能。
后者主张,使⽤更加动态的类型系統,分析代码更为简单,减少出错机会,才能更加轻松快速的编写程序。
3. 动态语⾔(动态编程语⾔)
3.1 定义
根据,动态(编程)语⾔的定义如下:
动态编程语⾔是⾼级编程语⾔的⼀个类别,在计算机科学领域已被⼴泛应⽤。它是⼀类在运⾏时可以改变其结构的语⾔,例如新的函数、对象、甚⾄代码可以被引进,已有的类、函数也可以被删除或是
其他结构上的变化。
动态语⾔⽬前⾮常具有活⼒,例如 JavaScript 便是⼀个动态语⾔,除此之外如 PHP、Ruby、Python 等也都属于动态语⾔,⽽
python解释器下载C、C++ 等语⾔则不属于动态语⾔。
3.2 python动态语⾔的体现
动态语⾔是⼀门在运⾏时可以改变其结构的语⾔,这句话如何理解?
⽰例 1:运⾏过程中给(实例)对象添加属性
class Person(object):
def __init__(self, name=None, age=None):
self.name = name
self.age = age
Jack = Person("Jack",18)
print(Jack.age)
在上述代码中,我们定义了 Person 类,然后创建了 Jack 对象,打印对象的 age 属性,这没⽑病。现实中⼈除了名字和年龄,还会有其他属性,例如⾝⾼和体重。我们尝试打印⼀下⾝⾼属性。
print(Jack.height)
毫⽆疑问,这会报错,因为 Person 类中没有定义 height 属性。
但是如果在程序运⾏的时候添加 height 属性,会发⽣什么呢?
Jack.height = 170
print(Jack.height) # 输出结果:170
setattr(Jack, 'height', 170)
print(Jack.height) # 输出结果:170
在上述代码中,我们给 Jack 添加了 height 属性,然后打印,没有报错,可以输出结果。
我们再打印⼀下对象的属性:
print(Jack.__dict__) # 输出结果:{'name': 'Jack', 'age': 18, 'height': 170}
本来对象是没有 height 属性,但是可以在程序运⾏过程中给实例对象动态绑定属性,这就是动态语⾔的魅⼒。
需要注意:
Mia = Person('Mia', 18)
print(Mia.__dict__) # 输出结果:{'name': 'mia', 'age': 18}
Mia 对象居然没有 height 属性。为什么?事实上我们只是给类⽰例动态地绑定了⼀个属性,⽽不是给类绑定属性,所以重新创建的对象是没有 height 属性的。如果想要给类添加,也是可以的。
⽰例 2:动态给类添加属性
Person.height = None
Mia = Person("Mia", 18)
print(Mia.height) # 输出结果:None
⽰例 3:动态给对象添加⽅法
class Person(object):
def __init__(self,name=None,age=None):
self.name = name
self.age = age
def speak_name(self):
print(self.name)
Jack = Person("Jack", 18)
Jack.speak_name = speak_name
Jack.speak_name(Jack) # Jack
print(Jack.__dict__) # {'name': 'Jack', 'age': 18, 'speak_name': <function speak_name at 0x000001F86CAE1E18>}
Mia = Person("Mia", 18)
print(Mia.__dict__) # {'name': 'Mia', 'age': 18}
在上述代码中,对象 Jack 的属性中已经成功添加了 speak_name 函数。但是!有没有感觉 Jack.speak_name(Jack) 这个语句很别扭。按习惯来说,应该 Jack.speak_name() 就⾏了。如果想要达到这种效果,应该要像下⾯这样⼦做:
import types
Jack.speak_name = types.MethodType(speak_name, Jack)
Jack.speak_name() # 输出结果:Jack
其中 MethodType ⽤于绑定⽅法对象。
⽰例 4:动态给类添加⽅法
import types
class Person(object):
def __init__(self, name=None, age=None):
self.name = name
self.age = age
def speak_ok(cls):
print(OK)
Person.speak_name = types.MethodType(speak_ok, Person)
Person.speak_ok() # OK
⽰例 5:动态删除属性/⽅法
Mia = Person("Mia", 18)
delattr(Mia,'height') # 等价于 del Mia.height
print(Mia.__dict__)
# 输出结果:{'name': 'mia', 'age': 18}
总结
给实例对象添加属性/⽅法:对象名.属性/⽅法名 = xxxx
给类对象添加属性/⽅法:类名.属性/⽅法名 = xxxx
给实例/类对象删除属性/⽅法:
del 实例/类对象.属性/⽅法名
delattr(实例/类对象, "属性/⽅法名")
3.3 __slots__()
通过以上例⼦可以得出⼀个结论:相对于动态语⾔,静态语⾔具有严谨性!所以,玩动态语⾔的时候,⼩⼼动态的坑!
如果我们想要限制实例对象的属性怎么办?⽐如,只允许对 Person 的实例对象添加 name 和 age 属性。
为了达到限制的⽬的,Python 允许在定义类的时候,定义⼀个 __slots__() ⽅法,来限制该实例对象能添加的属性:
>>> class Person:
... __slots__ = ("age", "name")
...
>>> p = Person()
>>> p.age = 12
>>> p.name = "xiaoming"
>>> p.hobby = "football"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Person' object has no attribute 'hobby'
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论