Frame Object 对 Python 的执行至关重要,它是 Python 模块、函数、推导式等多级代码组织形式的根本支撑。除了在 C 层面跟踪 Frame Object 结构体外,在 Python 中也提供了封装,可以帮助我们轻松掌握 Frame 的情况。
读取 Frame Object
sys 模块提供了 _getframe 方法用户返回当前 frame object 的封装,通过下面的代码获得 module 的 frame:
import sys
def func():
print("func call")
return 1
a = 1
# get frame
frame = sys._getframe()
print("------- dir:")
print(dir(frame))
print("-------- locals:")
print(frame.f_locals)
输出:
------- dir:
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'f_back', 'f_builtins', 'f_code', 'f_globals', 'f_lasti', 'f_lineno', 'f_locals', 'f_trace', 'f_trace_lines', 'f_trace_opcodes']
-------- locals:
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7673ab50>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'demo_get_frame.py', '__cached__': None, 'sys': <module 'sys' (built-in)>, 'func': <function func at 0x7678b7c8>, 'a': 1, 'frame': <frame at 0x7676cc30, file 'demo_get_frame.py', line 17, code <module>>}
可以看到 frame 对象是 Frame Object 的封装,成员都非常熟悉了。顺便输出了 f_locals 成员,可以看到所有内置变量和自行创建的变量和它们的值。
操作 Frame Object
实际上我们甚至可以直接操作 Frame Object,比如直接在 Frame 中新增 local 变量。
frame.f_locals["s"] = "hello world"
print(f"new var s, value is {s}")
上面的代码也创建了变量 s,并且可以在之后与其他变量无差别的使用。我们已经大概可以看到 Frame Object 对 Python 的重要性。在一个 scope 中,所有的操作都是对当前 Frame 的操作。Python 在底层真的直接使用了 Python 代码中的所有命名,这与 C 这样的编译语言会在编译后丢失绝大多数命名信息完全不同。String、Dict 构成 Python 执行的基础。
总结
后面的分析中,我们可以直接在 Python 代码中直接访问 Frame Object,并配合之前提到的反汇编技术探索 Python VM 的实现细节。