Rumah pembangunan bahagian belakang Tutorial Python 初步探究Python程序的执行原理

初步探究Python程序的执行原理

Jun 10, 2016 pm 03:15 PM
python

1. 过程概述

Python先把代码(.py文件)编译成字节码,交给字节码虚拟机,然后虚拟机一条一条执行字节码指令,从而完成程序的执行。
2. 字节码

字节码在Python虚拟机程序里对应的是PyCodeObject对象。
.pyc文件是字节码在磁盘上的表现形式。
3. pyc文件

PyCodeObject对象的创建时机是模块加载的时候,即import。
Python test.py会对test.py进行编译成字节码并解释执行,但是不会生成test.pyc。
如果test.py加载了其他模块,如import util,Python会对util.py进行编译成字节码,生成util.pyc,然后对字节码解释执行。
如果想生成test.pyc,我们可以使用Python内置模块py_compile来编译。
加载模块时,如果同时存在.py和.pyc,Python会尝试使用.pyc,如果.pyc的编译时间早于.py的修改时间,则重新编译.py并更新.pyc。
4. PyCodeObject

Python代码的编译结果就是PyCodeObject对象。

typedef struct {
  PyObject_HEAD
  int co_argcount;    /* 位置参数个数 */
  int co_nlocals;     /* 局部变量个数 */
  int co_stacksize;    /* 栈大小 */
  int co_flags;  
  PyObject *co_code;   /* 字节码指令序列 */
  PyObject *co_consts;  /* 所有常量集合 */
  PyObject *co_names;   /* 所有符号名称集合 */
  PyObject *co_varnames; /* 局部变量名称集合 */
  PyObject *co_freevars; /* 闭包用的的变量名集合 */
  PyObject *co_cellvars; /* 内部嵌套函数引用的变量名集合 */
  /* The rest doesn't count for hash/cmp */
  PyObject *co_filename; /* 代码所在文件名 */
  PyObject *co_name;   /* 模块名|函数名|类名 */
  int co_firstlineno;   /* 代码块在文件中的起始行号 */
  PyObject *co_lnotab;  /* 字节码指令和行号的对应关系 */
  void *co_zombieframe;  /* for optimization only (see frameobject.c) */
} PyCodeObject;
 
typedef struct {
  PyObject_HEAD
  int co_argcount;    /* 位置参数个数 */
  int co_nlocals;     /* 局部变量个数 */
  int co_stacksize;    /* 栈大小 */
  int co_flags;  
  PyObject *co_code;   /* 字节码指令序列 */
  PyObject *co_consts;  /* 所有常量集合 */
  PyObject *co_names;   /* 所有符号名称集合 */
  PyObject *co_varnames; /* 局部变量名称集合 */
  PyObject *co_freevars; /* 闭包用的的变量名集合 */
  PyObject *co_cellvars; /* 内部嵌套函数引用的变量名集合 */
  /* The rest doesn't count for hash/cmp */
  PyObject *co_filename; /* 代码所在文件名 */
  PyObject *co_name;   /* 模块名|函数名|类名 */
  int co_firstlineno;   /* 代码块在文件中的起始行号 */
  PyObject *co_lnotab;  /* 字节码指令和行号的对应关系 */
  void *co_zombieframe;  /* for optimization only (see frameobject.c) */
} PyCodeObject;
 

Salin selepas log masuk

5. pyc文件格式

加载模块时,模块对应的PyCodeObject对象被写入.pyc文件,格式如下:

2015411111617086.png (377×480)

6. 分析字节码
6.1 解析PyCodeObject

Python提供了内置函数compile可以编译Python代码和查看PyCodeObject对象,如下:

Python代码[test.py]

s = ”hello”

def func():
  print s

func()
 
s = ”hello”
 
def func():
  print s
 
func()

Salin selepas log masuk

在Python交互式shell里编译代码得到PyCodeObject对象:

2015411111701972.png (658×147)

dir(co)已经列出co的各个域,想查看某个域直接在终端输出即可:

2015411111733180.png (542×74)

test.py的PyCodeObject

co.co_argcount  0
co.co_nlocals   0
co.co_names    (‘s', 'func')
co.co_varnames  (‘s', 'func')
co.co_consts   (‘hello', <code object func at 0x2aaeeec57110, file ”test.py”, line 3>, None)
co.co_code    'd\x00\x00Z\x00\x00d\x01\x00\x84\x00\x00Z\x01\x00e\x01\x00\x83\x00\x00\x01d\x02\x00S'
 
co.co_argcount  0
co.co_nlocals   0
co.co_names    (‘s', 'func')
co.co_varnames  (‘s', 'func')
co.co_consts   (‘hello', <code object func at 0x2aaeeec57110, file ”test.py”, line 3>, None)
co.co_code    'd\x00\x00Z\x00\x00d\x01\x00\x84\x00\x00Z\x01\x00e\x01\x00\x83\x00\x00\x01d\x02\x00S'
Salin selepas log masuk

Python解释器会为函数也生成的字节码PyCodeObject对象,见上面的co_consts[1]

func的PyCodeObject

func.co_argcount  0
func.co_nlocals  0
func.co_names   (‘s',)
func.co_varnames  ()
func.co_consts   (None,)
func.co_code    ‘t\x00\x00GHd\x00\x00S'
 
func.co_argcount  0
func.co_nlocals  0
func.co_names   (‘s',)
func.co_varnames  ()
func.co_consts   (None,)
func.co_code    ‘t\x00\x00GHd\x00\x00S'
Salin selepas log masuk

co_code是指令序列,是一串二进制流,它的格式和解析方法见6.2。
6.2 解析指令序列

指令序列co_code的格式

2015411111800381.jpg (360×97)

Python内置的dis模块可以解析co_code,如下图:

test.py的指令序列

2015411111825052.png (734×228)

func函数的指令序列

2015411111856558.png (404×103)

第一列表示以下几个指令在py文件中的行号;
第二列是该指令在指令序列co_code里的偏移量;
第三列是指令opcode的名称,分为有操作数和无操作数两种,opcode在指令序列中是一个字节的整数;
第四列是操作数oparg,在指令序列中占两个字节,基本都是co_consts或者co_names的下标;
第五列带括号的是操作数说明。
7. 执行字节码

Python虚拟机的原理就是模拟可执行程序再X86机器上的运行,X86的运行时栈帧如下图:

2015411111945075.png (470×610)

假如test.py用C语言来实现,会是下面这个样子:

const char *s = “hello”;

void func() {
  printf(“%s\n”, s);
}

int main() {
  func();
  return 0;
}
 
const char *s = “hello”;
 
void func() {
  printf(“%s\n”, s);
}
 
int main() {
  func();
  return 0;
}

Salin selepas log masuk

Python虚拟机的原理就是模拟上述行为。当发生函数调用时,创建新的栈帧,对应Python的实现就是PyFrameObject对象。
7.1 PyFrameObject

typedef struct _frame {
  PyObject_VAR_HEAD
  struct _frame *f_back;  /* 调用者的帧 */
  PyCodeObject *f_code;   /* 帧对应的字节码对象 */
  PyObject *f_builtins;   /* 内置名字空间 */
  PyObject *f_globals;   /* 全局名字空间 */
  PyObject *f_locals;    /* 本地名字空间 */
  PyObject **f_valuestack; /* 运行时栈底 */
  PyObject **f_stacktop;  /* 运行时栈顶 */
  …….
}
 
typedef struct _frame {
  PyObject_VAR_HEAD
  struct _frame *f_back;  /* 调用者的帧 */
  PyCodeObject *f_code;   /* 帧对应的字节码对象 */
  PyObject *f_builtins;   /* 内置名字空间 */
  PyObject *f_globals;   /* 全局名字空间 */
  PyObject *f_locals;    /* 本地名字空间 */
  PyObject **f_valuestack; /* 运行时栈底 */
  PyObject **f_stacktop;  /* 运行时栈顶 */
  …….
}
Salin selepas log masuk

那么对应Python的运行时栈就是这样子:

2015411112025713.png (628×248)

7.2 执行指令

执行test.py的字节码时,会先创建一个栈帧,以下用f表示当前栈帧,执行过程注释如下:

test.py的符号名集合和常量集合

co.co_names  (‘s', 'func')
co.co_consts (‘hello', <code object func at 0x2aaeeec57110, file ”test.py”, line 3>, None)
 
co.co_names  (‘s', 'func')
co.co_consts (‘hello', <code object func at 0x2aaeeec57110, file ”test.py”, line 3>, None)
Salin selepas log masuk

test.py的指令序列

2015411112058949.png (561×361)

上面的CALL_FUNCTION指令执行时,会创建新的栈帧,并执行func的字节码指令,以下用f表示当前栈帧,func的字节码执行过程如下:

func函数的符号名集合和常量集合

func.co_names    (‘s',)
func.co_consts   (None,)
 
func.co_names    (‘s',)
func.co_consts   (None,)
 

Salin selepas log masuk

func函数的指令序列

2015411112126342.png (559×195)

7.3 查看栈帧

如果你想查看当前栈帧,Python提供了sys._getframe()方法可以获取当前栈帧,你只需要在代码里加入代码如下:

def func():
  import sys
  frame = sys._getframe()
  print frame.f_locals
  print frame.f_globals
  print frame.f_back.f_locals
  #你可以打印frame的各个域
  print s 
 
Salin selepas log masuk

 


Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn

Alat AI Hot

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

AI Hentai Generator

AI Hentai Generator

Menjana ai hentai secara percuma.

Artikel Panas

R.E.P.O. Kristal tenaga dijelaskan dan apa yang mereka lakukan (kristal kuning)
2 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
Repo: Cara menghidupkan semula rakan sepasukan
4 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island Adventure: Cara mendapatkan biji gergasi
4 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌

Alat panas

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina

SublimeText3 versi Cina

Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Bagaimana untuk menyelesaikan masalah kebenaran yang dihadapi semasa melihat versi Python di Terminal Linux? Bagaimana untuk menyelesaikan masalah kebenaran yang dihadapi semasa melihat versi Python di Terminal Linux? Apr 01, 2025 pm 05:09 PM

Penyelesaian kepada Isu Kebenaran Semasa Melihat Versi Python di Terminal Linux Apabila anda cuba melihat versi Python di Terminal Linux, masukkan Python ...

Bagaimana cara menyalin seluruh lajur satu data ke dalam data data lain dengan struktur yang berbeza di Python? Bagaimana cara menyalin seluruh lajur satu data ke dalam data data lain dengan struktur yang berbeza di Python? Apr 01, 2025 pm 11:15 PM

Apabila menggunakan Perpustakaan Pandas Python, bagaimana untuk menyalin seluruh lajur antara dua data data dengan struktur yang berbeza adalah masalah biasa. Katakan kita mempunyai dua DAT ...

Bolehkah anotasi parameter Python menggunakan rentetan? Bolehkah anotasi parameter Python menggunakan rentetan? Apr 01, 2025 pm 08:39 PM

Penggunaan alternatif anotasi parameter python Dalam pengaturcaraan Python, anotasi parameter adalah fungsi yang sangat berguna yang dapat membantu pemaju memahami dan menggunakan fungsi ...

Pembangunan Aplikasi Desktop Cross-Platform Python: Perpustakaan GUI mana yang terbaik untuk anda? Pembangunan Aplikasi Desktop Cross-Platform Python: Perpustakaan GUI mana yang terbaik untuk anda? Apr 01, 2025 pm 05:24 PM

Pilihan Perpustakaan Pembangunan Aplikasi Desktop Python Python Banyak pemaju Python ingin membangunkan aplikasi desktop yang boleh dijalankan pada kedua-dua sistem Windows dan Linux ...

Mengapa kod saya tidak dapat mendapatkan data yang dikembalikan oleh API? Bagaimana menyelesaikan masalah ini? Mengapa kod saya tidak dapat mendapatkan data yang dikembalikan oleh API? Bagaimana menyelesaikan masalah ini? Apr 01, 2025 pm 08:09 PM

Mengapa kod saya tidak dapat mendapatkan data yang dikembalikan oleh API? Dalam pengaturcaraan, kita sering menghadapi masalah mengembalikan nilai null apabila panggilan API, yang bukan sahaja mengelirukan ...

Bagaimanakah uvicorn terus mendengar permintaan http tanpa serving_forever ()? Bagaimanakah uvicorn terus mendengar permintaan http tanpa serving_forever ()? Apr 01, 2025 pm 10:51 PM

Bagaimanakah Uvicorn terus mendengar permintaan HTTP? Uvicorn adalah pelayan web ringan berdasarkan ASGI. Salah satu fungsi terasnya ialah mendengar permintaan HTTP dan teruskan ...

Bagaimanakah skrip Python jelas output ke kedudukan kursor di lokasi tertentu? Bagaimanakah skrip Python jelas output ke kedudukan kursor di lokasi tertentu? Apr 01, 2025 pm 11:30 PM

Bagaimanakah skrip Python jelas output ke kedudukan kursor di lokasi tertentu? Semasa menulis skrip python, adalah perkara biasa untuk membersihkan output sebelumnya ke kedudukan kursor ...

Adakah Google dan AWS menyediakan sumber imej Pypi awam? Adakah Google dan AWS menyediakan sumber imej Pypi awam? Apr 01, 2025 pm 05:15 PM

Ramai pemaju bergantung kepada PYPI (PythonPackageIndex) ...

See all articles