开发者

limiting number of optional, positional arguments to a function/method

what is an appropriate solution to limit the number of optional, positional arguments for a function or method? E.g. I'd like to have a function that takes either two or three positional arguments (but not more). I cannot use an optional keyword argument (because the function needs to accept an unlimited number of arbitrarily named keyword arguments). What I've come up with so far is something like this:

def foo(x, y, *args, **kwargs):
    if len(args) == 1:
        # do some开发者_运维问答thing
    elif len(args) > 1:
        raise TypeError, "foo expected at most 3 arguments, got %d" % (len(args) + 2)
    else
        # do something else

Does this reasonable or is there a better way?


One way to find out what is consider "pythonic" is to search for examples in the python source code itself.

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

If you peruse the results of the above command (without the wc), you'll find plenty of examples using exactly the technique you propose.


this works:

>>> 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 {}


Your solution seems reasonable to me.


You can write a decorator:

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

And then use it like this:

>>> @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
>>> 


It looks good to me.

If you want to abstract that logic out of the body of the function you can put it in a decorator:

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)

The output of this code is:

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.


Am I late? I do it this way:

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

Check PEP 3102 -- Keyword-Only Arguments for further reference.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜