1.1 Python中的类
Python作为一门面向对象的高级编程语言,提供了丰富的面向对象编程的实现,包括面向对象语言中的类、对象。对于任意一门面向对象的高级编程语言,最基础的特性都是封装、继承和多态,而实现这些特性的基础正是面向对象编程语言中的类。
类是真实世界中的事务在Python语言中的一种实现,其规定了真实世界中的事务在Python语言中的组成,是使用Python来描绘真实世界中事务的手段。在真实世界中,事务可能是一个非常大的问题,也可能是一个非常小的问题,即在真实世界中,事务本身不是一个定数,所以,Python中类的设计也是如此。
Python中的类规定了真实世界中的事务在Python中的定义和实现,我们可以通过以下代码定义Python中的类:
执行上述代码即可创建一个名为[className]的Python类。在Python中存在一个全局解释器,该解释器用来执行Python代码。Python解释器将处理类的过程全部执行完毕后,通过上述代码创建的Python类才能被真正创建。Python中的类在被创建之后,在类的同一生命周期下,就不允许继续修改了,因为该类已经被转义为Python解释器可识别的代码,这些代码已经被解释和执行了。如果需要继续修改该Python类,我们可以先在该Python类中编写需要修改的内容,然后手动执行并重新解释。
在了解了Python类的创建过程和解释过程之后,我们真正创建一个Python类来进一步了解Python类的组成。根据上述创建类的代码,我们创建一个名为HelloPython的类,并且在HelloPython类中先定义两个成员变量strA和strB,再定义两个方法:一个方法被声明为Hello,另一个方法被声明为World。创建HelloPython类的代码如下所示。
我们再来看一下HelloPython类所在的目录结构,以PyCharm代码编辑器为例,HelloPython所在目录结构如图1-1所示。
图1-1 HelloPython所在目录结构
这里是在一个名为highPro的项目中创建HelloPython类。highPro项目是本书所使用的项目,该项目会在后文进行介绍。
通过图1-1可知,HelloPython类所在的Python文件名为HelloWorld,并不是HelloPython,这在Python语言中是允许的,但是在Java语言中会直接报错,连编译都不能。这就是Python语言和Java语言最显著的区别。
Python解释器在解释Python代码时,会先对Python代码进行编译,在编译通过之后,才会将编译的Python代码交给Python解释器(虚拟机)来执行,这是Python代码解释的全过程,而在这个过程中会有不同类型的文件产出。我们以HelloPython类为例展开介绍,如图1-2所示。
图1-2 HelloPython类代码执行过程
通过图1-2可知,HelloPython类会先被Python编译器进行编译。在编译阶段,Python编译器会检查HelloPython类代码是否符合Python语言所规定的语法格式和语义规范,还会检查各种变量的定义和引用等。只有这些检查项全部通过,编译才能通过,这些检查项中只要有一项存在异常或错误,Python编译器就会立即中断编译,向用户抛出异常或错误。重复该过程,直到编译通过。
在HelloPython类编译通过后会输出HelloPython类字节码文件,如图1-3所示。
图1-3 HelloPython类字节码文件
通过图1-3可知,HelloPython类生成的字节码文件名为HelloWorld.cpython-39.pyc,大小为1KB。HelloPython类字节码文件名称由4部分组成。
● HelloWorld:表示Python文件的名称,即HelloPython类所在的Python文件的名称。
● cpython:表示HelloPython类被哪种虚拟机编译,本书使用的是Python默认实现的CPython虚拟机,所以这里是cpython。
● 39:表示当前Python版本在CPython虚拟机中对应的字节码版本号,该版本号默认由采用的Python版本的第一位大版本号和第二位小版本号组成,忽略其余位数的版本号。本书采用的Python版本是3.9.5,取前两位来表示这一字节码版本号,忽略后面的5,所以这里是39。
● pyc:这是文件的后缀名,表示当前的文件类型是Python字节码文件,而不是Java字节码文件。Java字节码文件名以javac结尾。
接着将HelloPython类字节码文件交由CPython虚拟机处理。CPython虚拟机的主要工作是解析HelloPython类字节码文件,并根据该字节码文件中的内容为HelloPython类中的各种变量分配内存空间,为各种方法创建执行所需的栈帧空间。如果该类中存在类的实例,CPython虚拟机会为该类的实例分配内存空间,并初始化该类的实例的其他属性。下面介绍HelloPython类字节码文件中的底层内容,以便更好地理解Python类字节码文件,如图1-4所示。
图1-4 HelloPython类字节码文件中的底层内容
这里我们只需要看3个部分。第一部分是图1-4所示的前8位,即610D0D0A。这部分是Python字节码的第一部分,即Python语言中的魔数。CPython虚拟机根据这8位内容判断当前需要处理的字节码文件是不是Python字节码文件。如果一个字节码文件的头内容中包含610D0D0A,就表示该字节码文件是Python字节码文件,此时CPython虚拟机才会继续向下解析该文件,否则会终止解析,并向用户抛出异常或错误。CPython虚拟机所能识别的Python字节码的魔数,同样会随着Python版本而发生改变,并不是固定不变的。
第二部分是图1-4所示的第8列到第B列的内容,即DA808C62。这8位表示Python字节码文件头的大小。我们可以使用数据解释器计算出该类字节码文件头的大小,如图1-5所示。
第三部分是Offset,即偏移量从00000000往下一直到该文件结束(不包含00000000)的内容,这部分就是HelloPython类中的字段、方法或者实例被编译成字节码之后的内容。
图1-5 HelloPython类字节码文件头的大小
回到我们平常所说的Python代码解释过程,结合笔者对HelloPython类代码的解析过程可以得出,Python语言中所说的解释器其实就是Python编译器和Python虚拟机结合的产物,即Python代码的编译和Python虚拟机的处理是同一时机触发的,只不过这个过程没有对外暴露而已。