在Python中反向执行函数

3 投票
3 回答
2175 浏览
提问于 2025-04-17 22:02

我有一个函数,看起来像这样:

def f():
  call_some_function_A()
  call_some_function_B()
  [...]
  call_some_function_Z()

我希望这个函数能够反向执行,也就是说,执行的顺序应该像这样:

def f'():
  call_some_function_Z()
  [...]
  call_some_function_B()
  call_some_function_A()

(f 的设计是可以逻辑上反向执行的;也就是说,没有变量声明或其他类似的东西)。

我该怎么做呢?

我不能仅仅写一个函数 f',让它反向调用 f 中的语句,因为每次 f 改变时,我都不想去更新 f'

我也不能修改 f

(请不要告诉我不应该尝试这样做,或者重新设计我的代码,或者其他类似的建议——这对我来说是不可能的。)

3 个回答

1

我简单拼凑了一个小函数,这个函数假设输入的是一串简单的单行语句。它使用了exec这个东西,实际上是eval的另一种形式,所以这让代码的编译变得有点困难。不过,如果你能接受这种被执行的代码,那就看看这个吧:

import inspect

# sample function that will be reversed
def f(): 
  print "first statement"
  print "2nd statement"
  print "last statement"

def makeReversedFunctionSrc(newName, f):
    src = inspect.getsource(f)
    srcLines = src.split("\n")
    srcLines = srcLines[1:] # get rid of the old function definition
    srcLines.reverse() # reverse function body
    # hack together new function definition with reversed lines
    newSrc = "def " + newName + "():\n"
    for line in srcLines:
        if line.strip() != "":
            newSrc += line + "\n"
    return newSrc

# get the code as a string
reverseCode = makeReversedFunctionSrc("reversedF", f)
# execute the string as if it was python (I heard thats evil as in eval)
exec(reverseCode)

# now lets call our new function
reversedF()
11

如果你的 f() 函数里面全是一些函数调用,你可以把它改成一个列表:

functions = [
    call_some_function_A,
    call_some_function_B,
#   [...]
    call_some_function_Z,
]

然后可以用这个列表来按相反的顺序调用这些函数。

def f():
    for func in functions:
        func()

def f_():
    for func in reversed(functions):
        func()
4

请不要这样做。


如果你的 f() 函数完全由这些函数调用组成:

def f():
    call_some_function_A()
    call_some_function_B()
#   [...]
    call_some_function_Z()

...你可以破解它,获取它引用的所有名称:

names = f.__code__.co_names
# ('call_some_function_A', 'call_some_function_B', 'call_some_function_Z')

但你仍然需要找到对应的函数。

如果这些函数在其他模块或者类似的地方,你可以这样做:

functions = [getattr(some_module, name) for name in names]

如果这些函数在同一个文件里作为全局变量定义,那就这样做:

functions = [globals()[name] for name in names]
# [<function __main__.call_some_function_A>, <function __main__.call_some_function_B>, <function __main__.call_some_function_Z>]

然后你只需要按相反的顺序调用它们:

def f_():
    for func in reversed(functions):
        func()

另外,你还可以获取函数的源代码,解析它,反转抽象语法树,再编译回去,执行它……这样你就能得到反转后的函数。

我们来看这个例子:

def f():
    call_some_function_A()
    if whatever:
        call_some_function_B()
        call_some_function_C()
    call_some_function_D()
import inspect
import ast

original_f = f

source = inspect.getsource(f)
tree = ast.parse(source)
# tree is a Module, with body consisting of 1 FunctionDef
# tree.body[0] is a FunctionDef, with body consisting of Exprs
tree.body[0].body.reverse() 
# top level expressions will be reversed

# compile the modified syntax tree to a code object as a module and execute it
exec(compile(tree, '<unknown>', 'exec'))
# f will be overwritten because the function name stays the same

# now f will be equivalent to:
#   def f():
#       call_some_function_D()
#       if test:
#           call_some_function_B()
#           call_some_function_C()
#       call_some_function_A()

f_ = f
f = original_f

所以,是的,这种方法稍微好一点。甚至可以递归地反转所有的 body,实现对 ...B...C 的反转,但如果引入了即使是最简单的逻辑代码,你 肯定 会遇到麻烦。

撰写回答