【问题标题】:generic function in python - calling a method with unknown number of argumentspython中的通用函数-调用具有未知数量参数的方法
【发布时间】:2011-11-16 18:55:57
【问题描述】:

我是 python 新手,需要一些帮助...

我正在实现一个接受参数“边缘”的通用搜索函数,它可以是多种类型的数据结构。

在搜索方法中我有一行:

 fringe.push(item, priority)

问题在于不同数据结构中的 push 方法采用不同数量的参数(有些需要优先级,有些不需要)。有没有一种优雅的方法可以通过它并使“推送”方法仅从发送的参数列表中获取它需要的参数数量?

谢谢!

【问题讨论】:

    标签: python generics


    【解决方案1】:

    获得不同数量的参数并且仍然能够选择正确的方法是使用 *args 和 **keyword_args 参数。 来自 Mark Lutz 的 Learning Python 书:

    * 和 ** 旨在支持采用任意数量参数的函数。两者都可以出现在函数定义或 函数调用,在这两个地方都有相关的用途。

    * 和 ** 在函数定义中

    如果你定义一个函数:

    def f1(param1, *argparams, **kwparams): 
        print 'fixed_params -> ', param1
        print 'argparams  --> ', argparams
        print 'kwparams   ---->,', kwparams
    

    你可以这样称呼它:

    f1('a', 'b', 'c', 'd', kw1='keyw1', kw2='keyw2')
    

    然后你得到:

    fixed_params ->  a
    argparams  -->  ('b', 'c', 'd')
    kwparams   ---->, {'kw1': 'keyw1', 'kw2': 'keyw2'}
    

    这样您就可以发送/接收任意数量的参数和关键字。 恢复关键字 args 的一种典型习语如下:

    def f1(param1, **kwparams):
        my_kw1 = kwparams['kw1']
        ---- operate  with my_kw1 ------
    

    通过这种方式,您可以使用任意数量的参数调用您的函数,并使用它需要的参数。
    这种类型或参数经常用于一些 GUI 代码,如 wxPython 类定义和子类化以及函数柯里化、装饰器等

    * 和 ** 在函数调用中

    函数调用中的* 和** 参数在被函数获取时会被解包:

    def func(a, b, c, d):
        print(a, b, c, d)
    
    args = (2, 3)
    kwargs = {'d': 4}
    func(1, *args, **kwargs)
    
    ### returns ---> 1 2 3 4 
    

    太棒了!

    【讨论】:

    • 这如何解决我认为是关于调用可能接受一个或两个参数但调用者不知道哪个参数的现有函数的问题?
    • @Duncan 如果你不知道要发送哪些参数,那么要么内省方法,要么尝试尝试/失败,要么发送所有可能的已知参数并让不同的对象方法获取他们需要的。最后一种方法很简单,在许多情况下都相当普遍。对于 OP 示例,您只需使用关键字默认参数即可解决问题。由于我不确定 OP 是在谈论 一个或两个 论点还是更一般的 一个或多个 论点,所以我给了他一个总体观点或 *args 和 **卡格斯。希望他觉得有用
    • 这是一个很好的答案,如果您可以更改您正在调用的函数的定义。如果没有,像@Duncan's answer之类的东西似乎是必要的。库开发人员可以通过使用户定义的回调方法采用 *args 和 **kwargs 来避免这个问题。这将允许您在将来无限期地向回调添加参数,而不会破坏任何内容或需要检查回调。
    【解决方案2】:

    理论上,您可以使用inspect.getargspec(fringe) 找出该方法采用的参数。这将告诉您可以传递的参数数量,但它非常混乱:

    argspec = inspect.getargspec(fringe.push)
    if len(argspec.args) >= 3 or argspec.varargs or argspec.keywords:
        fringe.push(item, priority)
    else:
        fringe.push(item)
    

    如果有必要,只要去争取并请求宽恕就简单多了:

    try:
        fringe.push(item, priority)
    except TypeError:
        fringe.push(item)
    

    当然更好的是确保push() 方法都具有一致的参数规范,但如果您不能这样做,那么请使用try...except。

    【讨论】:

    • 这种方法对我来说是必要的情况是在处理我想要保持旧回调签名向后兼容的库时。在这种情况下,我有点担心捕获 TypeError,因为我不知道它是来自回调签名还是来自用户回调代码中的某些异常。
    【解决方案3】:

    试试下面的代码sn-ps,

    def push(item, priority=None):
        print item,priority
    
    
    args = (1,)
    push(*args)
    
    args = (1,2)
    push(*args)
    

    【讨论】:

      【解决方案4】:

      你不能只使用默认参数值吗?

      >>> def foo(a, b = 10):
      ...   print a, b
      ... 
      >>> foo(1000)
      1000 10
      >>> foo(1000, 1000)
      1000 1000
      >>>
      

      如果没有提供参数b,则默认为10。

      【讨论】:

      • 如果我理解正确 - 这需要更改边缘可以是的每个数据结构中的推送方法......因为我希望搜索功能是通用的,我希望它可以与现有数据一起使用结构。操作需要在搜索方法本身中。有什么好的方法吗?
      • 您必须查看哪些数据类型需要该参数,哪些不需要。您可以(不推荐)使用try, except 块并尝试在没有参数的情况下运行.push。如果它坏了,试试这个参数。不过,我怀疑它会很快。
      猜你喜欢
      • 2020-12-06
      • 1970-01-01
      • 2022-08-09
      • 1970-01-01
      • 2012-08-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-27
      相关资源
      最近更新 更多