如何从函数外部获取函数的参数和值?

5 投票
2 回答
4206 浏览
提问于 2025-04-16 15:45

我搜索了一下,想弄明白这个问题,但没有找到完全符合我需求的解决方案。

这是我的使用场景:

  • 我想在函数被调用时,从外部评估函数的文档字符串中的表达式,并将其与函数的参数和参数值进行比较,但不想在函数内部执行时进行评估。
  • 我不能静态地修改我正在评估的源代码(不能直接在里面写新功能),但可以动态地改变(比如在运行时包装函数或者添加属性)是可以接受的。
  • 我更倾向于使用标准库中的工具,但如果外部库能让这个任务变得简单,我也愿意尝试。

下面是我想做的一个简单示例:

def f1(a,b):
    """a==b"""
    pass

def f2(f):
    f_locals = "get f's args and values before f is executed"
    return eval(f.__doc__,None,f_locals)

>>> f2(f1(2,2))

2 个回答

1

我不确定这是不是你想要的,不过这里有一个不使用inspect模块的替代方案。

#!/usr/bin/python
# -*- coding: utf-8-unix -*-
"""
This is a sample implementation of Inline.pm (Perl) in Python.

Using @inline decorator, it is now possible to write any code
in any language in docstring, and let it compile down to executable
Python code at runtime.

For this specific example, it simply evals input docstring, so
code in docstring must be in Python as well.
"""

# Language compiler for MyLang
class MyLang:
    @classmethod
    def compile(self, docstring):
        # For this example, this simply generates code that
        # evals docstring.
        def testfunc(*arg, **kw):
            return eval(docstring, None, kw)
        return testfunc

# @inline decorator
def inline(lang):
    def decorate(func):
        parm = func.__code__.co_varnames[0:func.__code__.co_argcount]
        fgen = lang.compile(func.__doc__)
        def wrap(*arg, **kw):
            # turn all args into keyword-args
            kw.update(dict(zip(parm, arg)))
            return fgen(**kw)
        return wrap
    return decorate

@inline(MyLang)
def myadd(a, b):
    """a + b"""

print(myadd(1, 9))
print(myadd(b = 8, a = 2))
print(myadd(a = 3, b = 7))
5

虽然我不太明白你为什么想这么做,但你描述的内容可以通过inspect模块来实现。这个例子是我能想到的最接近你原始例子的代码。

from inspect import getcallargs
def f1(a,b):
   """a==b"""
   pass

def f2(f, *f_args, **f_kwargs):
    f_callargs = getcallargs(f, *f_args, **f_kwargs)
    return eval(f.__doc__, None, f_callargs)

f2(f1, 2, 2)

这段代码应该会输出True

需要注意的是,这段代码假设了很多关于传给f2的函数参数和文档字符串的情况,最重要的是,它假设这些被检查的函数都没有恶意或错误的地方。你为什么不想正常调用函数,为什么不想改变函数呢?

编辑:正如Pajton指出的,getcallargs在这里更合适,并且去掉了对dictzip的调用。上面的代码已经更新以反映这一点。

撰写回答