如何列出Python函数期望的变量?

13 投票
3 回答
10052 浏览
提问于 2025-04-17 20:18

我在想,能不能在调用一个Python函数之前,先列出这个函数需要的变量,这样我就可以从一个包含很多变量的大字典里,传递这些需要的变量。

我在网上搜索过,但没找到相关的信息。不过,Python解释器可以显示出需要的变量列表,所以肯定有办法在脚本里做到这一点吧?

3 个回答

1

简单明了:

import inspect  #library to import  
def foo(bar, baz, spam='eggs', *monty, **python): pass  #example function

argspec = inspect.signature(foo)
print(argspec) #print your output

输出结果是: (bar, baz, spam='eggs', *monty, **python)

这个方法在类里面的方法中也能用(非常有用!):

class Complex: #example Class
     def __init__(self, realpart, imagpart): #method inside Class
...         self.r = realpart
...         self.i = imagpart

argspec = inspect.signature(Complex)
print(argspec)

输出结果是: (realpart, imagpart)

2

作为一个附带的回答,我现在使用另一种方法来传递函数所需的变量:我把所有变量都传过去。

我的意思是,我在根对象中维护一个全局/共享的变量字典(这个根对象是所有其他对象的父对象),例如:

shareddict = {'A': 0, 'B':'somestring'}

然后我只需将这个字典传递给任何需要调用的其他对象的方法,就像这样:

shareddict.update(call_to_func(**shareddict))

正如你所看到的,我们将shareddict中的所有键值对展开,作为关键字参数传递给call_to_func()。我们还会用返回的结果更新shareddict,下面会解释原因。

现在使用这种技术,我可以简单明了地在我的函数/方法中定义是否需要这个字典中的一个或多个变量:

my_method1(A=None, *args, **kwargs):
''' This method only computes on A '''
    new_A = Do_some_stuff(A)
    return {'A': new_A} # Return the new A in a dictionary to update the shared value of A in the shareddict

my_method2(B=None, *args, **kwargs):
''' This method only computes on B '''
    new_B = Do_some_stuff(B)
    return {'B': new_B} # Return the new B in a dictionary to update the shareddict

my_method3(A=None, B=None, *args, **kwargs):
''' This method swaps A and B, and then create a new variable C '''
    return {'A': B, 'B': A, 'C': 'a_new_variable'} # Here we will update both A and B and create the new variable C

你会注意到,上面所有的方法都返回一个变量字典,这个字典会更新shareddict,并且会传递给其他函数。

这种技术有几个优点:

  • 实现起来相当简单
  • 优雅地维护一个共享的变量列表,但不需要使用全局变量
  • 函数和方法在定义时清楚地显示它们需要什么(当然有一个注意事项,即即使是必需的变量也需要设置为带有默认值(如None)的关键字参数,这通常意味着这个变量是可选的,但在这里并不是)
  • 这些方法可以被继承和重载
  • 内存占用低,因为同一个shareddict会一直传递
  • 子函数/方法定义它们需要什么(自下而上),而不是根对象定义将传递给子对象的参数(自上而下)
  • 创建/更新变量非常简单
  • 可选地,将所有这些变量导出到文件中非常简单,例如使用json.dumps(finaldict, sort_keys=True)
21

你可以使用 inspect.signature() 或者 inspect.getfullargspec() 这两个函数:

import inspect

argspec = inspect.getfullargspec(somefunction)
signature = inspect.signature(somefunction)

inspect.fullargspec 会返回一个包含7个元素的命名元组:

  • 一个包含参数名称的列表
  • 如果定义了一个接收所有位置参数的 *args 参数,它的名称;否则为 None
  • 如果定义了一个接收所有关键字参数的 **kwargs 参数,它的名称;否则为 None
  • 一个元组,里面是关键字参数的默认值;这些默认值与参数的最后几个元素对应,数量要一致。
  • 一个只接受关键字参数的参数名称列表
  • 如果有的话,包含只接受关键字参数的默认值的字典
  • 还有一个包含注解的字典

使用 inspect.signature() 你会得到一个 Signature 对象,这个对象不仅把上面的数据以更结构化的方式呈现出来,还可以像调用函数一样把值绑定到参数上。

哪个更好取决于你的具体需求。

示例:

>>> import inspect
>>> def foo(bar, baz, spam='eggs', *monty, python: "kwonly", spanish=42, **inquisition) -> "return annotation":
...     pass
... 
>>> inspect.getfullargspec(foo)
FullArgSpec(args=['bar', 'baz', 'spam'], varargs='monty', varkw='inquisition', defaults=('eggs',), kwonlyargs=['python', 'spanish'], kwonlydefaults={'spanish': 42}, annotations={'return': 'return annotation', 'python': 'kwonly'})
>>> signature = inspect.signature(foo)
>>> signature
<Signature (bar, baz, spam='eggs', *monty, python: 'kwonly', spanish=42, **inquisition) -> 'return annotation'>
>>> signature.parameters['python'].kind.description
'keyword-only'
>>> signature.bind('Eric', 'Idle', 'John', python='Cleese')
<BoundArguments (bar='Eric', baz='Idle', spam='John', python='Cleese')>

如果你有一个名为 values 的字典,里面是可能的参数值,我会使用 inspect.signature(),并利用 Signature.parameters 映射来匹配名称:

posargs = [
    values[param.name]
    for param in signature.parameters.values()
    if param.kind is Parameter.POSITIONAL_ONLY
]
skip_kinds = {Parameter.POSITIONAL_ONLY, Parameter.VAR_POSITIONAL, Parameter.VAR_KEYWORD}
kwargs = {
    param.name: values[param.name]
    for param in signature.parameters.values()
    if param.name in values and param.kind not in skip_kinds
}

上面的操作会给你一个位置参数的值列表,以及一个字典用于其他参数(不包括任何 *args**kwargs 参数)。

撰写回答