代码之家  ›  专栏  ›  技术社区  ›  gaborous

在Python中列出函数所需的变量?

  •  11
  • gaborous  · 技术社区  · 13 年前

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

    我在网上搜索过,但什么也找不到。然而,python解释器可以显示预期变量的列表,所以在脚本中肯定有一些方法可以做到这一点?

    3 回复  |  直到 13 年前
        1
  •  18
  •   Martijn Pieters    5 年前

    您可以使用 inspect.signature() inspect.getfullargspec() 功能:

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

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

    • 包含参数名称的列表
    • 陷阱的名称 *args 参数,如果已定义( None 否则)
    • 陷阱的名称 **kwargs 参数,如果已定义( 没有一个 否则)
    • 一个元组,具有关键字参数的默认值;它们与论点的最后元素一致;通过默认值元组的长度来匹配这些值。
    • 仅限关键字的参数名称列表
    • 仅关键字参数名称的默认值字典(如果有的话)
    • 以及包含注释的词典

    具有 检查签名() 你得到了一个 Signature object ,一个丰富的对象,它不仅将上述数据建模为一组更结构化的对象,还允许您像调用函数一样将值绑定到参数。

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

    演示:

    >>> 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 可能的参数值,我会使用 检查签名() 并使用 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
    }
    

    上面给出了仅位置参数的值列表,以及其他参数的字典(任何 *参数 **夸尔格 参数)。

        2
  •  2
  •   gaborous    7 年前

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

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

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

    然后我简单地将这个dict传递给要调用的任何其他对象的任何方法,就像这样:

    shareddict.update(call_to_func(**shareddict))
    

    正如您所看到的,我们将shareddict中的所有键/值解压为call_to_func()的关键字参数。我们还用返回的结果更新了shareddict,我们将在下面看到原因。

    现在有了这个技术,如果我需要这个dict中的一个或多个变量,我可以在函数/方法中简单而清晰地定义:

    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
    

    正如您所注意到的,上面的所有方法都返回一个变量dict,它将更新shareddict,并将其传递给其他函数。

    这种技术有几个优点:

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

    简单明了:

    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='ggs',*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)