python运行机制总结

主要参考:

python运行机制 - 仙人小麦 - 博客园 (cnblogs.com)

给编程初学者:从解释器到python的运行机制 - 知乎 (zhihu.com)

python解释器 

python是一门解释型语言,但还不够确切,其实许多解释型语言在正式处理代码前,都是先需要内部进行预处理的。

Python解释器由编译器和虚拟机构成。

执行 Python 程序(.py文件)时,Python 内部先隐式地将源代码 (source code) 编译成字节码。这一步我们会发现生成了一种.pyc文件,.pyc文件中存储着的就是python程序编译后的字节码。这种.pyc文件具备可移植性,不管在哪一个平台都可以使用。运行时将由虚拟机逐行把字节码翻译成机器码,这才能被计算机直接执行。

解释器是一种让其他程序运行起来的程序,它是代码与机器的计算机硬件之间的软件逻辑层,Python解释器就是能够让Python程序在机器上执行的一套程序。 当我们执行写好的Python代码时,Python解释器会执行两个步骤

1、

把原始代码编译成字节码

编译后的字节码是特定于Python的一种表现形式,它不是二进制的机器码,需要进一步编译才能被机器执行,这也是Python代码无法运行的像C/C++ 一样快的原因。如果Python进程在机器上拥有写入权限,那么它将把程序的字节码保存为一个以.pyc 为扩展名的文件,如果Python无法在机器上写入字节码,那么字节码将会在内存中生成并在程序结束时自动丢弃。在构建程序的时候最好给Python赋上在计算机上写的权限,这样只要源代码没有改变,生成的.pyc文件可以重复利用,提高执行效率。

2、

把编译好的字节码转发到Python虚拟机(PVM)中进行执行

PVM是 Python Virtual Machine的简称,它是Python的运行引擎,是Python系统的一部分,它是迭代运行字节码指令的一个大循环、一个接一个地完成操作。

Python 解释器的几种实现版本

Python解释器有三种主要的实现方式,CPython、Jython和IronPython 三种实现方式 。

1、

CPython

CPython 是标准的实现,其它的都是有特定目标的。 CPython 是由C语言编写的,它是大多数Linux和Mac OS X机器预装的Python解释器,也是所有Python解释器中运行最快、最完整、最健全的。

2、

Jython

Jython 是一种Python语言的替代实现方式,其目的是为了与Java编程语言集成,Jython 包含了Java类,这些类编译Python源代码、形成Java字节码,并将得到的字节码映射到Java虚拟机(JVM)上。因为Jython要比CPython 慢而且也不够健壮,它往往看作是一个主要面向寻找Java代码前端脚本语言的Java开发者的一个有趣的工具。

3、

IronPython

IronPython 设计的目的是让Python 程序可以与Windows 平台上的.NET 框架以及与之对应的Linux的上开源的Mono编写成的应用集成。

4、

IPython

IPython是基于CPython之上的一个交互式解释器,也就是说,IPython只是在交互方式上有所增强,但是执行Python代码的功能和CPython是完全一样的。好比很多国产浏览器虽然外观不同,但内核其实都是调用了IE。

CPython用>>>作为提示符,而IPython用In [序号]:作为提示符。

5、

PyPy

PyPy是另一个Python解释器,它的目标是执行速度。PyPy采用JIT技术,对Python代码进行动态编译(注意不是解释),所以可以显著提高Python代码的执行速度。

绝大部分Python代码都可以在PyPy下运行,但是PyPy和CPython有一些是不同的,这就导致相同的Python代码在两种解释器下执行可能会有不同的结果。如果你的代码要放到PyPy下执行,就需要了解PyPy和CPython的不同点

Psyco 实时编译器

psyco 系统不是Python的另一种实现方式,而是Python字节码执行模块的一个扩展组件,它可以让程序运行的更快。它是一个PVM的增强工具,这个工具收集并使用信息,在程序运行时,可以将部分程序的字节码转换成底层的真正的二进制机器代码,从而实现更快的执行速度。在开发个过程中,Psyco无需代码的修改或独立的编译步骤即可完成这一转换。 概括地讲,就是当程序运行时,Psyco收集了正在传递过程中的对象的类别信息,这些信息可以用来裁剪对象的类型,从而生成高效的机器代码。机器代码一旦生成,就替代了对应的原始字节码,从而加快程序的整体执行速度。一些通过Psyco优化的Python代码的执行速度可以像编译好的C代码一样快。 因为字节码的转换与程序运行同时发生,所以Pysco往往被看做是一个即时编译器(JIT)。实际上Psyco是一个专有的JIT编译器:它生成机器代码将数据类型精简至你程序实际上所使用的类型。 Psyco是用纯Python的算法代码实现的。这点尤为重要,那些为了优化往往需要迁移到C的那部分代码,使用了Psyco后,这样的迁移就没有必要了。

Python解释器将源码转换为字节码,然后再由解释器来执行这些字节码。因此总的来说,它具有以下三条特性

  1. 源码距离底层更远(根据官方文档的解释)

  2. 运行时都需要生成字节码,交由虚拟机执行。(就是解释器。虚拟机具体实现了由switch-case语句构成的框架函数PyEval_EvalFrameEx,刚刚说的字节码就是这货执行的)

  3. 每次执行脚本,虚拟机总要多出加载和链接的流程。(所以呢,相比于编译型语言就有点慢了

虚拟机它是怎么执行脚本的

  1. 完成模块的加载和链接;

  2. 将源代码翻译为PyCodeObject对象(这货就是字节码),并将其写入内存当中(方便CPU读取,起到加速程序运行的作用);

  3. 从上述内存空间中读取指令并执行;

  4. 程序结束后,根据命令行调用情况(即运行程序的方式)决定是否将PyCodeObject写回硬盘当中(也就是直接复制到.pyc或.pyo文件中);

  5. 之后若再次执行该脚本,则先检查本地是否有上述字节码文件。有则执行,否则重复上述步骤。

更多参考:

【Python】新手入门学习:什么是python解释器以及它的作用和特性是什么_python解释器是什么-CSDN博客

python程序从哪里开始执行

参考:python-从哪里开始执行?_python程序是从哪里开始运行的-CSDN博客

如果你经常使用的是C语言或者其他高级语言,当你去开始阅读代码的时候,你总会不自觉的去找main函数,但是当你初次接触Python的时候可能会感觉到不习惯,因为Python没有main主函数。

  • python 代码的执行不依赖于 main()函数;
  • python 代码从没有缩进的代码开始执行。

python使用缩进对齐组织代码的执行,所有没有缩进的代码(非函数定义和类定义),都会在载入时自动执行,这些代码,可以认为是Python的main函数。

每个文件(模块)都可以任意写一些没有缩进的代码,并且在载入时自动执行,为了区分主执行文件还是被调用的文件,因此python就引入了一个机制。

if __name__ == "__main__":

如果我们在程序里面添加如上的代码,编译器就能识别出从哪里开始。这句话的含义:如果这个文件被别的文件当作模块调用了,那么此时name 就会变为文件的名字,否则默认是main,那么整个工程就会从这句话下面的没有缩进的代码开始执行。说的可能比较拗口,直接看个例子吧。

if __name__ == "__main__":  
    print("Hello World!")  
    def test():  
        print("test")  
    def main():  
        print("main")  
    main()  

执行结果

Hello World!
main 

可以看到代码先打印的不是main因为,程序从if name == “main“:下面没有缩进的代码开始执行,类和函数不在内。先打印了Hello World!,然后执行main函数打印出main;

再看看另一篇文章:「Python 」 python 从哪开始执行 之 main 函数_python程序从main开始-CSDN博客

初次接触Python的人会很不习惯Python没有main主函数。 这里简单的介绍一下,在Python中使用main函数的方法

#hello.py
def foo():
    str="function"
    print(str);
if __name__=="__main__":
    print("main")
    foo()

其中if __name__=="__main__":这个程序块类似与Java和C语言的中main(主)函数

在Cmd中运行

C:\work\python\divepy>python hello.py
main
function

在Python Shell中运行

>>> import hello
>>> hello.foo()
function
>>> hello.__name__
'hello'
>>>

可以发现这个内置属性__name__自动的发生了变化。 这是由于当你以单个文件运行时,__name__便是__main__ 当你以模块导入使用时,这个属性便是这个模块的名字。

在C/C++/Java中,main是程序执行的起点,Python中,也有类似的运行机制,但方式却截然不同:Python使用缩进对齐组织代码的执行,所有没有缩进的代码(非函数定义和类定义),都会在载入时自动执行,这些代码,可以认为是Python的main函数。

每个文件(模块)都可以任意写一些没有缩进的代码,并且在载入时自动执行,为了区分主执行文件还是被调用的文件,Python引入了一个变量__name__,当文件是被调用时,__name__的值为模块名,当文件被执行时,__name__为'__main__'。这个特性,为测试驱动开发提供了极好的支持,我们可以在每个模块中写上测试代码,这些测试代码仅当模块被Python直接执行时才会运行,代码和测试完美的结合在一起。

总结:

1). python 代码的执行不依赖于 main() 函数;

2). python 代码从没有缩进的代码开始执行。

例子1:

$ cat main.py 
#! /usr/bin/env python
 
"""
@file main.py
@brief main.py for testing python main entry.
"""
 
import os,sys
import time
 
if __name__ == "__main__":
        print("Hello python !!!")
 
        def func_print():
                print("in func_print")
 
        def main():
                print("In main")
 
        main()

运行结果

 
$ ./main.py 
Hello python !!!
In main

例子2:

 
$ cat main2.py 
#! /usr/bin/env python
 
"""
@file main.py
@brief main.py for testing python main entry.
"""
 
import os,sys
import time
 
print("Hello python !!!")
 
def func_print():
        print("in func_print")
 
def main():
        print("In main")
 
#main()
func_print()

运行结果:

$ ./main2.py 
Hello python !!!
in func_print

疑问:import会执行吗?

可参考:python导入包以及Python程序执行顺序理解_from config.config import *-CSDN博客

对于一个Python程序,首先执行的是import程序,特别地,如果import了别的包,那么会跳入到别的包模块先执行。

import os          # 1

print('<[1]> time module start')        # 2


class ClassOne():
    print('<[2]> ClassOne body')            # 3

    def __init__(self):                     # 10
        print('<[3]> ClassOne.__init__')

    def __del__(self):
        print('<[4]> ClassOne.__del__')     # 101

    def method_x(self):                     # 12
        print('<[5]> ClassOne.method_x')

    class ClassTwo(object):
        print('<[6]> ClassTwo body')        # 4


class ClassThree():
    print('<[7]> ClassThree body')          # 5

    def method_y(self):                     # 16
        print('<[8]> ClassThree.method_y')  


class ClassFour(ClassThree):
    print('<[9]> ClassFour body')           # 6


def func():
    print("<func> function func")

if __name__ == '__main__':                      # 7
    print('<[11]> ClassOne tests', 30 * '.')    # 8
    one = ClassOne()                            # 9
    one.method_x()                              # 11
    print('<[12]> ClassThree tests', 30 * '.')  # 13
    three = ClassThree()                        # 14
    three.method_y()                            # 15
    print('<[13]> ClassFour tests', 30 * '.')  # 17
    four = ClassFour()
    four.method_y()

print('<[14]> evaltime module end')             # 100
  • 首先执行#1的import语句
  • 执行#2的打印语句
  • ClassOne、ClassThree和ClassFour的类定义执行过程中,分别打印#3、#4、#5、#6四句话,但是其中的方法并不执行,仅仅是载入内存
  • 碰到#7的if name == ‘main’:,判断为True,于是执行if内部的代码
  • 执行#8的print语句
  • 执行#9,实例化一个ClassOne的对象
  • 执行#10的初始化方法,打印一条语句
  • 返回执行#11的menthod_x调用
  • 返回类的定义体,找到#12,执行方法,打印语句
  • 再返回#13处,打印
  • 执行#14的实例化
  • ClassThree没有自定义初始化方法,接着执行#15
  • 回到类里找到#16的方法,执行打印语句
  • 执行#17

…后面不再详述

执行完最后的#100的打印语句后,按理说程序应该终止退出了,但由于ClassOne这个类定义了__del__方法,还要在最后执行它内部的代码#101这条打印语句。

通过这个例子,相信你对Python的程序执行流程能够有一定的了解。其实这个过程,也是我们读别人代码的过程。

更多补充:python import导入时,发生了什么?_python import一个包时发生了哪些操作-CSDN博客

python在导入模块时,即import时究竟有哪些动作?在python中,导入并非只是把一个文本文件插入到另一个文件。导入其实是运行时的运算,程序第一次导入指定文件时,会执行以下三个步骤:

1、找到模块文件

2、编译成位码(即pyc文件)

3、执行模块的代码来创建其所定义的变量(你没看错,导入时代码是会执行的)

需要明确的事,模块导入只有在第一次导入时才会进行。此后,导入相同模块时,会跳过这三个步骤,而只提取内存中已经加载的模块对象。从技术上,Python把载入的模块储存在一个名为sys.modules的表中,并在第一次导入操作时,检查该表。如果不存在便会启动这三个过程。

小结

虽然我们有各种流程判断、循环、跳转、控制、中断等等,但从根本上程序还是逐行顺序执行的。

Python代码在执行过程中,遵循下面的基本原则(可参考计算机系统相关知识理解):

普通语句,直接执行;

碰到函数,将函数体载入内存,并不直接执行

碰到类,执行类内部的普通语句,但是类的方法只载入,不执行

碰到if、for等控制语句,按相应控制流程执行

碰到@,break,continue等,按规定语法执行

碰到函数、方法调用等,转而执行函数内部代码,执行完毕继续执行原有顺序代码

内置模块if name=='main’

在一个py程序中,经常会看到if name==‘main’;其中__name__本身是一个内置模块,py文件通常有两大类执行方式,要么是直接运行,比如直接在IDE窗口中点run或者Python 目标.py;要么是被调用运行;在直接运行的时候,该程序的内置属性__name__的值就会是__main__;但py文件作为被调用的对象的时候,该值就不为__main__;

  • 25
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值