首先我想说,我知道这是一个可怕的想法,不应该这样做。我的目的主要是好奇和学习 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)
[]和{}被编译为分别专门返回 alist或 a的特定操作码dict。另一方面list(),并dict()编译为搜索全局变量的字节码list,dict然后将它们作为函数调用:
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操作码并将其替换为适当的操作码。尽管这些操作码的大小不同,但您可能还需要进行一些额外的更改。