948 字
5 分钟
CodeObject
2025-08-10
无标签

记录一下关于 python CodeObject 的一些知识点

什么是 Code Object#

Code Object 是 Python 内部用来表示编译后代码的数据结构. 当 Python 解释器将源代码编译成字节码时,会创建 Code Object 来存储这些字节码以及相关的元数据. 每个函数、类、模块都会生成对应的 Code Object.
访问函数的__code__属性即可获取到对应的CodeObject对象

def func(a):
    print(a)
print(func.__code__)
# <code object func at 0x104836f10, file "/Users/asteriax/Desktop/codes/temp/2.py", line 1>

Code Object 的属性#

查看某个函数对应的Code Object的属性#

def func(a):
    print(a + "sth")

for attr in dir(func.__code__):
    if attr.startswith('co_'):
        print(f"{attr}:\t{getattr(func.__code__, attr)}")

运行,即可看到一个类似这样的输出

co_argcount:    1
co_cellvars:    ()
co_code:        b'\x95\x00[\x01\x00\x00\x00\x00\x00\x00\x00\x00U\x00S\x01-\x00\x00\x005\x01\x00\x00\x00\x00\x00\x00 \x00g\x00'
co_consts:      (None, 'sth')
co_exceptiontable:      b''
co_filename:    /Users/asteriax/Desktop/codes/temp/1.py
co_firstlineno: 1
co_flags:       3
co_freevars:    ()
co_kwonlyargcount:      0
co_lines:       <built-in method co_lines of code object at 0x1056e2f10>
co_lnotab:      b'\x02\x01'
co_name:        func
co_names:       ('print',)
co_nlocals:     1
co_positions:   <built-in method co_positions of code object at 0x1056e2f10>
co_posonlyargcount:     0
co_qualname:    func
co_stacksize:   4
co_varnames:    ('a',)

Code Object各个属性的含义#

co_argcount#

表示函数的位置参数数量
这里顺便介绍一下Python中的位置参数和关键字参数的概念

位置参数(Positional Arguments):调用函数时,按照参数在定义时的位置顺序传递的参数
关键字参数(Keyword Arguments):调用函数时,通过“参数名=值”的方式传递的参数,顺序可以与定义时不同

例如:

def func(a, b, c, /, d, *, e, f=6, **kwargs):
    """
    a, b, c: 位置参数
    /: 仅限位置参数分隔符
    d: 位置或关键字参数
    *: 仅限关键字参数分隔符
    e: 仅限关键字参数
    f: 仅限关键字参数(有默认值)
    **kwargs: 可变关键字参数
    """
    pass

codeobj = func.__code__
print(f"{codeobj.co_argcount}")  # 4 (a,b,c,d)
print(f"{codeobj.co_posonlyargcount}")  # 3 (a,b,c)
print(f"{codeobj.co_kwonlyargcount}")  # 2 (e,f)

co_posonlyargcount#

表示仅限位置参数的数量

co_kwonlyargcount#

表示仅限关键字参数的数量

co_varnames#

包含所有局部变量和参数的名字,按顺序排列

co_consts#

函数体中用到的所有常量组成的元组,包括字面量、字符串、None等
例如,如果函数体中出现 print(a + “sth”) ,使用了 “sth” 这个字符串常量,“sth”对应地就会出现在 co_consts 里

co_names#

函数体中用到的所有全局变量名、函数名等组成的元组

co_code#

字节码指令的二进制表示

co_filename#

文件名

co_name#

代码对象的名字(如函数名) 如果有一个函数 def foo():,那么他对应的 co_name 就是字符串 “foo”

co_firstlineno#

代码对象在源文件中的起始行号 例如,如果函数在文件的第 1 行开始,co_firstlineno 就是 1

co_flags#

标志位,描述函数的特性(如是否有 *args、**kwargs 等)

co_stacksize#

表示执行代码时需要的最大栈大小

co_freevars#

自由变量(在当前作用域外部定义的、但被当前作用域引用的变量)的名称

co_cellvars#

函数内被嵌套作用域引用的局部变量的名称(如果一个函数内有嵌套函数并且引用了外部函数的局部变量,那么这些外部变量会出现在 co_cellvars 中)

co_lnotab#

行号表,用于字节码和源码行号的映射

co_exceptiontable#

异常处理表

co_qualname#

函数的完整限定名称 (包括模块和类(如果是类方法)路径的名称)
例如,如果一个函数定义在 xxx 模块中的 yyy 类下,co_qualname 会返回 “xxx.yyy.foo”

co_nlocals#

函数中使用的局部变量和形参的数量

通过修改 Code Object 改变函数行为#

函数替换#

def foo():
  a = 1
  b = 2
  print(a+b)

def bar():
  pass

hack_co_keys = ["co_code", "co_consts", "co_names", "co_stacksize", "co_nlocals", "co_varnames"]
hack_co_attrs = { key: getattr(foo.__code__, key) for key in hack_co_keys }

bar.__code__ = bar.__code__.replace(**hack_co_attrs)

bar()

# output: 3

修改常量#

def func():
    with open('test', 'r') as f:
        print(f.read())

func()

print(func.__code__.co_consts)
dict = {"co_consts": (None, 'flag', 'r')}
func.__code__ = func.__code__.replace(**dict)

func()

# output:
# teststring
# (None, 'test', 'r')
# flag{thisisflag}
CodeObject
https://blog.asteriax.site/posts/codeobject/
作者
ASTERIAX
发布于
2025-08-10
许可协议
CC BY-NC-SA 4.0