zoukankan      html  css  js  c++  java
  • Python 解释器初探

    A Python Interpreter Written in Python 是一篇很棒的文章,作者用 Python 实现了一个 Python 解释器(Byterun),文章的前半部分讲解 Python 解释器的基本结构,实现一个玩具(简易指令集)解释器。

    有时候我们把 Python 解释执行的整个流程都叫做 Python 解释器,它包含以下几步:

    1. lexer
    2. parser
    3. compiler
    4. interpreter

    以源码文本作为输入,1, 2, 3 步后得到 code object,并作为第 4 步的输入,我们这里讨论的解释器只针对第 4 步。

    可能需要说明的是,为什么 Python 作为解释语言还有 compiler 呢,其实所谓解释语言,只是在编译步做相对(编译语言,如 C,Java)少的工作。

    从性质上说,Python 解释器是一个 virtual machine,也是一个 bytecode interpreter。而 virtual machine 又可以分为基于栈(stack)的和基于寄存器(register)的,Python 解释器属于前者。

    对于 Python 解释器的输入 code object,它是一个包含 bytecode 的对象,而 bytecode 是一组指令,是 Python 代码的 IR。

    7+5 为例,用一组玩具指令表示如下,其中 what_to_execute 就是 code objectinstructions 就是 bytecode

    what_to_execute = {
        "instructions": [
            ("LOAD_VALUE", 0), # the first number
            ("LOAD_VALUE", 1), # the second number
            ("ADD_TWO_VALUES", None),
            ("PRINT_ANSWER", None)
        ],
        "numbers": [7, 5]
    }
    

    再加上变量的处理,就可以得到这个玩具 Python 解释器了。

    class Interpreter:
        def __init__(self):
            self.stack = []
            self.environment = {}
    
        def LOAD_VALUE(self, number):
            self.stack.append(number)
    
        def PRINT_ANSWER(self):
            answer = self.stack.pop()
            print(answer)
    
        def ADD_TWO_VALUES(self):
            first_num = self.stack.pop()
            second_num = self.stack.pop()
            total = first_num + second_num
            self.stack.append(total)
    
        def STORE_NAME(self, name):
            val = self.stack.pop()
            self.environment[name] = val
    
        def LOAD_NAME(self, name):
            val = self.environment[name]
            self.stack.append(val)
    
        def parse_argument(self, instruction, argument, what_to_execute):
            """ Understand what the argument to each instruction means. """
            numbers = ["LOAD_VALUE"]
            names = ["LOAD_NAME", "STORE_NAME"]
    
            if instruction in numbers:
                argument = what_to_execute["numbers"][argument]
            elif instruction in names:
                argument = what_to_execute["names"][argument]
    
            return argument
    
        def run_code(self, what_to_execute):
            instructions = what_to_execute["instructions"]
            for each_step in instructions:
                instruction, argument = each_step
                argument = self.parse_argument(instruction, argument, what_to_execute)
    
                if instruction == "LOAD_VALUE":
                    self.LOAD_VALUE(argument)
                elif instruction == "ADD_TWO_VALUES":
                    self.ADD_TWO_VALUES()
                elif instruction == "PRINT_ANSWER":
                    self.PRINT_ANSWER()
                elif instruction == "STORE_NAME":
                    self.STORE_NAME(argument)
                elif instruction == "LOAD_NAME":
                    self.LOAD_NAME(argument)
    
        # better run_code making use of Python's dynamic method lookup
        def execute(self, what_to_execute):
            instructions = what_to_execute["instructions"]
            for each_step in instructions:
                instruction, argument = each_step
                argument = self.parse_argument(instruction, argument, what_to_execute)
                bytecode_method = getattr(self, instruction)
                if argument is None:
                    bytecode_method()
                else:
                    bytecode_method(argument)
    
    what_to_execute = {
        "instructions": [("LOAD_VALUE", 0),
                         ("STORE_NAME", 0),
                         ("LOAD_VALUE", 1),
                         ("STORE_NAME", 1),
                         ("LOAD_NAME", 0),
                         ("LOAD_NAME", 1),
                         ("ADD_TWO_VALUES", None),
                         ("PRINT_ANSWER", None)],
        "numbers": [1, 2],
        "names": ["a", "b"]}
    
    interpreter = Interpreter()
    interpreter.run_code(what_to_execute)
    

    在 Python 中对于一个函数对象 obj 我们可以使用 obj.__code__ 得到 code objectobj.__code__.co_code 得到 bytecode。但实际的输出可能是不可读的(字节),可以利用 Python dis 模块(bytecode disassembler)中的 dis.opname(n) 得到字节对应的字符串,也可以直接用 dis.dis(obj) 输出函数对象字节码的解释。

    后半部分过度到真实的 Python bytecode,其中关于 frames 的部分非常值得一读。

  • 相关阅读:
    entrySet()
    DBCC DBREINDEX重建索引提高SQL Server性能
    ASP中调用存储过程、语法、写法-sql server数据
    这个月一直很忙,没时间写点心得,就放点周末去玩的照片吧
    JQuery 中,使文本框获得焦点的方法
    数据库系统不能自动删除备份的原因之一
    C# 中的常用正则表达式总结
    删除在建表时SQL SERVER2000指定PRIMARY KEY引起的 聚合索引
    IIS7入门之旅:(1)appcmd命令的使用
    IIS7入门之旅:(2)如何实现和加载自定义的Basic Authentication模块
  • 原文地址:https://www.cnblogs.com/humz/p/11137264.html
Copyright © 2011-2022 走看看