取代Python的解析器功能?

Jac*_*nte 5 python built-in

首先我想说,我知道这是一个可怕的想法,不应该这样做。我的目的主要是好奇和学习 Python 的内部结构,以及如何“破解”它们。

我想知道是否有可能改变当我们使用[]创建列表时发生的情况。有没有一种方法可以修改解析器的行为方式,例如,导致["hello world"]调用print("hello world")而不是创建一个包含一个元素的列表?

我试图找到与此相关的任何文档或帖子,但未能成功。

下面是替换内置字典以使用自定义类的示例:

from __future__ import annotations
from typing import List, Any
import builtins


class Dict(dict):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.__dict__ = self

    def subset(self, keys: List[Any]) -> Dict:
        return Dict({key: self[key] for key in keys})


builtins.dict = Dict
Run Code Online (Sandbox Code Playgroud)

当导入此模块时,它将dict用类替换内置模块Dict。然而,这仅在我们直接调用时才有效dict()。如果我们尝试使用{}它将回退到基本的dict内置实现:

import new_dict

a = dict({'a': 5, 'b': 8})
b = {'a': 5, 'b': 8}

print(type(a))
print(type(b))
Run Code Online (Sandbox Code Playgroud)

产量:

<class 'py_extensions.new_dict.Dict'>
<class 'dict'>
Run Code Online (Sandbox Code Playgroud)

Chr*_*ris 3

[]{}被编译为分别专门返回 alist或 a的特定操作码dict。另一方面list(),并dict()​​编译为搜索全局变量的字节码listdict然后将它们作为函数调用:

import dis

dis.dis(lambda:[])
dis.dis(lambda:{})
dis.dis(lambda:list())
dis.dis(lambda:dict())
Run Code Online (Sandbox Code Playgroud)

返回(为了清楚起见,还添加了一些换行符):

  3           0 BUILD_LIST               0
              2 RETURN_VALUE

  5           0 BUILD_MAP                0
              2 RETURN_VALUE

  7           0 LOAD_GLOBAL              0 (list)
              2 CALL_FUNCTION            0
              4 RETURN_VALUE

  9           0 LOAD_GLOBAL              0 (dict)
              2 CALL_FUNCTION            0
              4 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)

因此,您可以dict()简单地通过覆盖 global 来覆盖返回的内容dict,但不能覆盖{}返回的内容。

这些操作码记录在此处。如果 BUILD_MAP 操作码运行,您会得到一个dict,没有办法绕过它。作为示例,以下是 CPython 中 BUILD_MAP 的实现,它调用函数_PyDict_FromItems。它不考虑任何类型的用户定义的类,它专门创建一个代表 python 的 C 结构体dict

至少在某些情况下,可以在运行时操作 python 字节码。如果您确实{}返回一个自定义类,我想您可以编写一些代码来搜索BUILD_MAP操作码并将其替换为适当的操作码。尽管这些操作码的大小不同,但您可能还需要进行一些额外的更改。