在Python中反向执行函数
我有一个函数,看起来像这样:
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
的反转,但如果引入了即使是最简单的逻辑代码,你 肯定 会遇到麻烦。