You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Python将代码字符串转为代码对象并替换已有函数实现?

问题描述

我需要修改一个函数的行为,但无法访问或修改该函数所在的文件。不过我可以import它,想要对其实现做一些修改。但由于无法访问源代码,我无法直接修改它。我希望真正改变该函数的实现,使得所有import它的其他文件也能使用新的函数。

我目前尝试操作函数的__code__.co_code属性的字节码(我知道这是非常不好的软件实践,但我真的别无选择)。__code__.co_code返回一系列字节字符,这非常难以理解和修改(我是说,怎么用字节写代码?)。我想向该函数注入一个新的代码对象。有没有办法先将包含新实现的Python字符串转换为字节序列,然后安全地注入到旧函数中?

作为最小可复现示例,假设我有以下函数:

def func1():
    return 1

我想将其修改为:

def func1():
    return 0

我已经成功访问了函数__code__.co_code属性的字节码序列,代码如下:

def func1():
    return 1

code_obj = func1.__code__.co_code
print(code_obj) # 输出 b'd\x01S\x00'
new_code = b'd\x01S\x00' # 复制了原始代码字节序列。但我该怎么用字节写`return 0`?
func1.__code__.co_code = new_code
print(func1)
code_obj2 = func1.__code__.co_code

但出现了如下错误:

Traceback (most recent call last):
  File "C:\Program Files\JetBrains\PyCharm 2024.1\plugins\python\helpers\pydev\pydevconsole.py", line 364, in runcode
    coro = func()
  File "<input>", line 7, in <module>
AttributeError: readonly attribute

错误提示我无法修改函数的__code__.co_code属性。此外,我真的不知道如何用字节序列编写新的函数实现(return 0)。我知道可能可以使用AST,但我也不知道如何使用。我真的被困住了。

补充说明:我不能用新实现遮蔽旧函数,因为我希望其他导入该库的文件也能生效。同时,我也不想通过import lib; lib.old_function = new_function的方式修改模块中的函数。


解决方案

首先,你碰到的AttributeError是因为__code__.co_code是只读属性,没法直接修改,但函数本身的__code__属性是可以替换的——这才是正确的修改路径,不用跟字节码死磕。

最简单的实现方式:用临时函数替换代码对象

你可以先写一个和目标行为一致的临时函数,然后把它的代码对象直接赋值给原函数的__code__:

def func1():
    return 1

# 先定义你想要的目标函数
def _temp_func():
    return 0

# 直接替换func1的代码对象
func1.__code__ = _temp_func.__code__

# 测试一下,现在会返回0
print(func1())

这样修改后,所有后续调用func1的地方(包括其他模块导入的)都会使用新的实现——前提是你在其他模块导入func1之前完成这个替换操作。

如果一定要手动处理字节码(不推荐)

要是你非得跟字节码打交道,先搞清楚原字节码的含义:b'd\x01S\x00'里,d\x01是LOAD_CONST 1(加载常量池里的第1个元素,也就是1),S\x00是RETURN_VALUE(返回结果)。要改成返回0,只需要把常量索引改成0,也就是字节码b'd\x00S\x00'。

但因为co_code只读,你需要用types.CodeType创建一个新的代码对象,再替换原函数的__code__:

import types

def func1():
    return 1

original_code = func1.__code__
# 复制原代码对象的所有参数,只替换co_code
new_code_obj = types.CodeType(
    original_code.co_argcount,
    original_code.co_posonlyargcount,
    original_code.co_kwonlyargcount,
    original_code.co_nlocals,
    original_code.co_stacksize,
    original_code.co_flags,
    b'd\x00S\x00',  # 改成返回0的字节码
    original_code.co_consts,
    original_code.co_names,
    original_code.co_varnames,
    original_code.co_filename,
    original_code.co_name,
    original_code.co_firstlineno,
    original_code.co_lnotab,
    original_code.co_freevars,
    original_code.co_cellvars
)

func1.__code__ = new_code_obj
print(func1())  # 输出0

这种方式需要严格对应CodeType的所有参数,稍有不慎就会出错,除非你对Python字节码非常熟悉,否则不建议用。

关于跨模块生效的说明

只要你在程序启动的最开始(比如主入口文件的最顶部)完成这个替换,之后任何模块导入func1都会拿到修改后的版本。比如如果func1在第三方库mylib里,你可以这么做:

# 主入口文件第一行
import mylib

def _temp_func():
    return 0

mylib.func1.__code__ = _temp_func.__code__

# 之后再导入其他模块
import other_module
# other_module里调用func1()会返回0

内容的提问来源于stack exchange,提问作者Luke L

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 10:45:01