2

python中有没有办法拦截(和更改)已编译函数的返回值?

基本思想是:我有一个函数

def a (n, acc):
    if n == 0: return acc
    return a (n - 1, acc + n)

我想修补它,使其行为如下:

def a (n, acc):
    if n == 0: return acc
    return lambda: a (n - 1, acc + n)

是否可以编写一个函数f,例如f (a)在第二个代码片段中生成一个函数?

我可以通过inspectif python 可以找到其源代码然后返回新编译的修补函数来修补该函数,但这无济于事。

4

3 回答 3

2

如果我正确理解你想要什么,理论上是不可能的;您似乎正在描述的转换会对等效函数产生不同的影响,具体取决于可能不会以编译形式保留的源代码的表面细节。例如,考虑给定函数的以下两个版本:

def a (n, acc):
    print('called a(%d,%d)' % (n, acc))
    if n == 0: return acc
    return a (n - 1, acc + n)

def a (n, acc):
    print('called a(%d,%d)' % (n, acc))
    if n == 0: return acc
    ret = a (n - 1, acc + n)
    return ret

显然它们在功能上是相同的。在源代码中,唯一的区别是前者return直接在某个表达式上使用,而后者将该表达式的结果保存到局部变量中,然后return在该变量上使用。在编译的形式中,根本不需要区别。

现在考虑“修补”版本:

def a (n, acc):
    print('called a(%d,%d)' % (n, acc))
    if n == 0: return acc
    return lambda: a (n - 1, acc + n)

def a (n, acc):
    print('called a(%d,%d)' % (n, acc))
    if n == 0: return acc
    ret = a (n - 1, acc + n)
    return lambda: ret

很明显,这些是非常不同的:例如,如果nis3并且acc是 0,那么前者打印called a(3,0)并返回一个函数,该函数打印called a(2,3)并返回一个函数,该函数打印called a(1,5)并返回一个函数,该函数打印called a(0,6)并返回6,而后者打印called a(3,0)andcalled a(2,3)called a(1,5)andcalled a(0,6)并且返回返回的函数返回的函数返回的函数6

更广泛的区别在于,第一个“修补”函数在每次调用新返回值时执行一个计算步骤,而第二个“修补”版本在初始调用期间执行计算的所有步骤,并简单地安排一系列为了娱乐的后续电话。只要有副作用(例如打印消息,或者递归太深以至于溢出堆栈),这种差异就会很重要。调用者是否引入副作用也很重要:请注意,这些函数只会递归,直到其他一些代码重新定义a,此时计划继续重新调用a的版本与已经完成了所有的调用。

由于您无法区分两个“未打补丁”版本,因此您显然无法生成转换所暗示的不同“打补丁”版本。

于 2013-02-03T07:35:23.013 回答
1

感谢您的意见。我没有看到明显的:

def inject (f):
    def result (*args, **kwargs):
        return lambda: f (*args, **kwargs)
    return result

我接受了 davidchambers 的回答,他把我推向了正确的方向。

于 2013-02-03T07:18:53.660 回答
0

卢克的回答非常详细,但这可能仍然有帮助:

>>> def f(*args, **kwargs):
...     return lambda: a(*args, **kwargs)
...
>>> f(10, 0)()
55
于 2013-02-03T07:08:03.227 回答