11

我想知道是否可以在调用 Python 函数之前列出 Python 函数预期的变量,以便从包含大量变量的更大字典中传递预期变量。

我已经搜索了网络,但找不到任何东西。但是,python 解释器可以显示预期变量的列表,所以肯定有某种方法可以在脚本中做到这一点?

4

3 回答 3

16

您可以使用inspect.signature()inspect.getfullargspec()函数:

import inspect

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

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

  • 带有参数名称的列表
  • 包罗万象的*args参数的名称,如果已定义(None否则)
  • 包罗万象的**kwargs参数的名称,如果已定义(None否则)
  • 具有关键字参数默认值的元组;它们与论点的最后一个元素一起使用;通过默认值元组的长度匹配这些。
  • 仅关键字参数名称列表
  • 仅关键字参数名称的默认值字典(如果有)
  • 和包含注释的字典

有了inspect.signature()一个Signatureobject,一个丰富的对象,它不仅将上述数据建模为一组更结构化的对象,而且还允许您将值绑定到参数,就像调用函数一样。

哪个更好将取决于您的用例。

演示:

>>> 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参数除外)。

于 2013-03-26T13:49:07.833 回答
2

作为一个侧面答案,我现在使用另一种方法将它们期望的变量传递给函数:我将它们全部传递。

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

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

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

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

如您所见,上述所有方法都返回一个变量字典,它将更新共享字典,并将其传递给其他函数。

这种技术有几个优点:

  • 实现起来很简单
  • 维护共享变量列表但不使用全局变量的优雅方式
  • 函数和方法在其定义中清楚地显示了它们的期望(但当然需要注意的是,即使是强制变量也需要设置为具有默认值(例如 None)的关键字参数,这通常意味着该变量是可选的,但在这里它不是
  • 方法是可继承和可重载的
  • 低内存占用,因为始终传递相同的 shareddict
  • 子函数/方法定义它们需要什么(自下而上),而不是根定义将传递给子函数的参数(自上而下)
  • 非常容易创建/更新变量
  • 可选地,将所有这些变量转储到文件中非常容易,例如使用json.dumps(finaldict, sort_keys=True).
于 2013-08-08T00:08:27.633 回答
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

prints: (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)

打印:(真实部分,想象部分)

于 2020-05-23T11:58:59.547 回答