gpt4 book ai didi

python - 正确替换函数的代码对象

转载 作者:太空宇宙 更新时间:2023-11-03 11:58:58 28 4
gpt4 key购买 nike

我正在尝试获取函数的源代码,向其中添加代码,然后将其放回原始函数中。

基本上是这样的:

new_code = change_code(original_code)
throwaway_module = ModuleType('m')
exec(new_code, throwaway_module.__dict__)
func.__code__ = getattr(throwaway_module, func.__name__).__code__

new_code 不包含任何不在原始函数中的名称时,这会完美地工作。

然而,当 new_code 包含原始 func 中不存在的变量名时,在最后一行出现以下错误:

ValueError: func() requires a code object with 1 free vars, not 0

有什么想法吗?

编辑:

我似乎找到了 CPython 源代码中引发此异常的位置(文件 funcobject.c)。为清楚起见省略了一些行:

static int
func_set_code(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignored))
{
Py_ssize_t nfree, nclosure;

// ... lines omitted

nfree = PyCode_GetNumFree((PyCodeObject *)value);
nclosure = (op->func_closure == NULL ? 0 :
PyTuple_GET_SIZE(op->func_closure));
if (nclosure != nfree) {
PyErr_Format(PyExc_ValueError,
"%U() requires a code object with %zd free vars,"
" not %zd",
op->func_name,
nclosure, nfree);
return -1;
}
Py_INCREF(value);
Py_XSETREF(op->func_code, value);
return 0;
}

这对你有帮助吗? :)

最佳答案

此异常是由于试图将一个代码对象分配给一个函数,该函数关闭的变量数量与其来源的函数不同。如果这句话听起来像胡言乱语,那么你应该看看 this answer .

避免此问题的最简单方法是以显而易见的方式简单地重新分配现有名称,即 f = g 而不是 f.__code__ = g.__code__。通过这种方式,代码对象始终与其匹配的闭包保持一致(稍后会详细介绍)。在您的情况下,这看起来像 func = getattr(throwaway_module, func.__name__)。有什么原因让您无法做到这一点,而是在处理内部实现细节吗?

为了更好地说明这里发生了什么,假设我们有一些愚蠢的功能。

def dog():
return "woof"

def cat():
return "meow"

def do_stuff(seq):
t1 = sum(seq)
seq2 = [e + t1 for e in seq]
t2 = sum(seq2)
return t1 + t2

def pair(animal):
def ret():
return animal() + animal()
return ret

cats = pair(cat)

print(dog()) # woof
print(cat()) # meow
print(cats()) # meowmeow
print(do_stuff([1,2,3])) # 30

即使 do_stuffdog 有不同数量的局部变量,我们仍然可以成功地在它们之间重新分配代码对象。

do_stuff.__code__ = dog.__code__
print(do_stuff()) # woof

但是,我们不能在 catsdog 之间重新分配,因为 cats 关闭参数 animal

print(cats.__code__.co_freevars) # ('animal',)
dog.__code__ = cats.__code__

ValueError: dog() 需要一个带有 0 个可用变量的代码对象,而不是 1 个

只需将名称重新分配给所需的函数对象即可避免此问题。

dog = cats
print(dog()) # meowmeow

事实上,如果您成功地为一个带有闭包的函数完成代码对象重新分配,那么如果执行该函数,事情很可能不会按预期进行。这是因为封闭变量与编译代码分开保存,因此它们不会匹配。

def get_sum_func(numbers):
def ret():
return sum(numbers)
return ret

sum_func = get_sum_func([2,2,2]) # sum_func closes over the provided arg

# swap code objects
# quite possibly the most disturbing single line of python I've ever written
sum_func.__code__, cats.__code__ = (cats.__code__, sum_func.__code__)

print(sum_func()) # this will attempt to execute numbers() + numbers(), which will throw
print(cats()) # this will attempt to execute sum(animal), which will throw

事实证明,我们无法轻易替换 __closure__ 属性,因为它是只读的。你大概可以 work around it如果你真的下定决心,那几乎可以肯定是个糟糕的主意。

# swap closures
# this results in "AttributeError: readonly attribute"
sum_func.__closure__, cats.__closure__ = (cats.__closure__, sum_func.__closure__)

有关函数对象属性的更多详细信息,请参阅 this answerthe docs .

关于python - 正确替换函数的代码对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54602320/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com