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

了解Python中的函数参数

  •  1
  • zakmck  · 技术社区  · 8 年前

    我试图理解以下关于处理函数及其参数的内容:

    def print_my_arg(func, *args, **kwargs):
        func(*args, **kwargs)
        if 'my_arg' in kwargs: 
            print('  my_arg = {}'.format(kwargs['my_arg']))
    
    def foo(call_no, my_arg='Default Value'):
        print('call_no = {}'.format(call_no) )
    
    print_my_arg(foo, 0, my_arg='My Value 1')
    print_my_arg(foo, 1, 'My Value 2')
    print_my_arg(foo, 2)
    

    输出:

    call_no = 0
      my_arg = My Value 1
    call_no = 1 # I'd like to see 'My Value 2' here
    call_no = 2 # I'd like to see 'Default Value' here
    

    显然,人们可以自由地用上面显示的任何一种方式调用函数,这让我想知道:为什么 my_arg 不会去 kwargs 无论如何难道没有一种统一的方式来按名称(而不是位置)访问参数吗?这不取决于函数的调用方式吗?

    请注意:

    1. 我不感兴趣 print_my_args(func, call_no, my_arg) ,因为我说的是我不知道签名的情况 func 但我想知道是否存在特定参数(按名称)。

    2. 显然,这与装饰器有关,但我已经用一种更简单的方式编写了这个示例(或者我希望如此)。

    编辑

    非常感谢您对检查签名的回答。使用它,我的新版本 print_my_arg() 是:

    from inspect import signature
    
    def print_my_arg ( func, *args, **kwargs ):
      func ( *args, **kwargs )
      sig = signature ( func )
      if 'my_arg' not in sig.parameters: return
    
      binding = sig.bind ( *args, **kwargs )
      binding.apply_defaults ()
    
      print ( "  my_arg = {}".format ( binding.arguments [ 'my_arg' ] ) )
    
    1 回复  |  直到 8 年前
        1
  •  3
  •   Tadhg McDonald-Jensen    8 年前

    是否有统一的方法按名称(而不是按 位置),这不取决于调用函数的方式?

    是,由 inspect 正在使用 signature :

    >>> import inspect
    >>> sig = inspect.signature(foo)
    >>> print(sig)
    (call_no, my_arg='Default Value')
    >>> args = sig.bind(1, "my_value")
    >>> args.arguments["my_arg"]
    'my_value'
    

    请注意,尝试将签名绑定到无效调用将引发类似/相同的TypeError,在使用无效参数调用函数时会引发该错误。使用默认值的参数也不会出现在 args.arguments 除非你打电话 args.apply_defaults()

    还要注意,仅关键字参数将位于 args.kwargs 而不是字典 args.arguments参数 :

    import inspect
    
    def bar(a,*, b=None):
        pass
    
    sig = inspect.signature(bar)
    
    binding = sig.bind(1, b=5)
    
    assert "a" in binding.arguments
    assert "b" in binding.kwargs