【问题标题】:limiting number of optional, positional arguments to a function/method限制函数/方法的可选位置参数的数量
【发布时间】:2009-11-11 22:15:37
【问题描述】:

什么是限制函数或方法的可选位置参数数量的适当解决方案?例如。我想要一个接受两个或三个位置参数(但不是更多)的函数。我不能使用可选的关键字参数(因为该函数需要接受无限数量的任意命名的关键字参数)。到目前为止,我想出的是这样的:

def foo(x, y, *args, **kwargs):
    if len(args) == 1:
        # do something
    elif len(args) > 1:
        raise TypeError, "foo expected at most 3 arguments, got %d" % (len(args) + 2)
    else
        # do something else

这样合理还是有更好的办法?

【问题讨论】:

    标签: python


    【解决方案1】:

    找出什么是“pythonic”的一种方法是在 python 源代码本身中搜索示例。

    find '/usr/lib/python2.6' -name '*.py' -exec egrep 'len\(args\)' {} + | wc
        156     867   12946
    

    如果您仔细阅读上述命令的结果(没有 wc),您会发现大量使用您建议的技术的示例。

    【讨论】:

      【解决方案2】:

      这行得通:

      >>> def foo(a, b, c=3, **kwargs):
          print(a, b, c, kwargs)
      
      
      >>> foo(3, 4, 2)
      3 4 2 {}
      >>> foo(3, 4)
      3 4 3 {}
      

      【讨论】:

      • 这也适用于 Py2k。我想我们都错过了这个简单的解决方案!
      • 不幸的是,def foo(a,b,c=3,d=4,e=5,**kwargs) 允许一个可选的位置参数,这是 NielsB 不想要的。
      • 这个有污染kwargs的问题,即c不能再用了
      • @NielsB:你能为你的变量选择足够独特的名字吗?顺便说一句,我相信 Python 不支持 无限 个参数。
      • @SilentGhost:我相信 NielsB 希望 foo 接受 0,2 或 3 个位置参数,而不是 1 个单独的位置参数。您的解决方案允许发送 foo 1 单独的位置参数。
      【解决方案3】:

      您的解决方案在我看来是合理的。

      【讨论】:

        【解决方案4】:

        你可以写一个装饰器:

        class TooManyArgumentsException(Exception):
            pass
        
        def limit_args(n):
            def limit_decorator(f):
                def new_f(*args, **kwargs):
                    if len(args) > n:
                        raise TooManyArgumentsException("%d args accepted at most, %d args passed" % (n, len(args)))
                    return f(*args, **kwargs)
                return new_f
            return limit_decorator
        

        然后像这样使用它:

        >>> @limit_args(5)
        ... def f(a, b, *args):
        ...     return a + b + sum(args)
        ...
        >>> f(1, 2, 3)
        6
        >>> f(1, 2, 3, 4)
        10
        >>> f(1, 2, 3, 4, 5)
        15
        >>> f(1, 2, 3, 4, 5, 6)
        Traceback (most recent call last):
          File "<stdin>", line 1, in <module>
          File "limit.py", line 8, in new_f
            raise TooManyArgumentsException("%d args accepted at most, %d args passed" % (n, len(args)))
        limit.TooManyArgumentsException: 5 args accepted at most, 6 args passed
        >>> 
        

        【讨论】:

        • 您可能想使用 functools.wraps 来保留文档字符串和名称。
        【解决方案5】:

        我觉得不错。

        如果你想从函数体中抽象出逻辑,你可以把它放在一个装饰器中:

        def validate_num_args(num_args=None, num_kwargs=None):
            def entangle(f):
                def inner(*args, **kwargs):
                    if not num_args is None and len(args) > num_args:
                        raise ValueError("Too many arguments, got %s, wanted %s." % (len(args), num_args))
                    if not num_kwargs is None and len(kwargs) > num_kwargs:
                        raise ValueError("Too many keyword arguments, got %s, wanted %s." % (len(kwargs), num_kwargs))
                    return f(*args, **kwargs)
                return inner
            return entangle
        
        @validate_num_args(num_args=3)
        def foo(x, y, *args, **kwargs):
            return "do something with these variables:", x, y, args, kwargs
        
        print "Good:\n", foo(1,2,3)
        print ""
        print "Bad:\n", foo(1,2,3,4)
        

        这段代码的输出是:

        Good:
        ('do something with these variables:', 1, 2, (3,), {})
        
        Bad:
        Traceback (most recent call last):
          File "c:\so.py", line 18, in <module>
            print "Bad:\n", foo(1,2,3,4)
          File "c:\so.py", line 5, in inner
            raise ValueError("Too many arguments, got %s, wanted %s." % (num_args, len(args)))
        ValueError: Too many arguments, got 4, wanted 3.
        

        【讨论】:

          【解决方案6】:

          我迟到了吗?我是这样做的:

          def (positional_1, positional_2, *, keyword_arguments...):
          

          请查看PEP 3102 -- Keyword-Only Arguments 以获取更多参考。

          【讨论】:

            猜你喜欢
            • 2015-08-22
            • 1970-01-01
            • 2018-02-18
            • 2023-04-02
            • 1970-01-01
            • 1970-01-01
            • 2018-06-28
            • 2018-07-21
            • 1970-01-01
            相关资源
            最近更新 更多