简体   繁体   中英

decorate a Python3 function to ignore *args in (a, b, *args, **kwargs)?

Python 3.3: Can I decorate a function to ignore all positional arguments that do not match named parameters?

import functools
def ignore_star_args():
    def wrapper(function_):
        @functools.wraps(function_)
        def wrapped(*f_args):
            return function_(/*only named args*/)
        return wrapped
    return wrapper

@ignore_star_args()
def list_args(a, b, *args):
    # two positional arguments
    return [a, b] + list(args)

>>> list_args(1, 2, "non-positional arg")
[1, 2]

I think you're misunderstanding the terms you're using.

Arguments are the values passed to a function call . Positional arguments are the ones without keywords.

Parameters are the names in a function definition . There are three different kinds of positional parameters; collectively, they're the ones up to and including the * parameter or bare * (if any).

For example, in this code:

def foo(a, b, c=3, d=4, *args, e, f=6, **kw): pass

foo(1, 2, d=3, c=4, e=5, g=7)

a , b , c , and d are all positional-or-keyword parameters ; args is a var-positional parameter ; e and f are keyword-only parameters ; and kw is a var-keyword parameter . (No examples of positional-only parameters , because they can't appear in a function definition; you have to write C/Java/.NET/RPython/whatever extension code, or create code objects manually, to get them.)

In the call, 1 and 2 are positional arguments ; 3 , 4 , 5 , and 7 are keyword arguments ; d , c , e , and g are keyword identifiers .

Python will match the two positional arguments to the first two positional-only or positional-or-keyword parameters, or to the var-positional argument if there aren't enough, then match the keyword arguments up to the positional-or-keyword or keyword-only parameters with the same names, or to the var-keyword argument if the names aren't found. So, inside foo :

a, b, c, d, args, e, f, kw = 1, 2, 4, 3, (), 5, 6, {'g': 7}

Now that you have all of that straight, let me sit a bit of confusion back in: occasionally Python uses different terminology, "formal argument" instead of "parameter" and "actual argument" instead of "argument". You mostly only find this deep in the oldest parts of the source code, but occasionally it bubbles up higher.


So, what you asked for what to ignore non-positional arguments. In other words, you want to accept but ignore keyword arguments. That's easy: just accept them with a var-keyword parameter, and don't pass them through:

def wrapped(*args, **kwargs):
    return function_(*f_args)

However, what I think you wanted to ask for was how to ignore all arguments, positional or keyword, that don't match positional-only or positional-or-keyword parameters.

To do that, you'll need to inspect the signature of the wrapped function.

The exact reference for what members different types have can be hard to find, and hard to understand when you do… but fortunately, the inspect documentation gathers it all together in a handy chart.

You can see that a function doesn't have anything that looks like its parameter signature… but it contains a code object, which does . Of course the code object was designed back when Python only had 2 kinds of parameters, and they were called "formal arguments" instead of "parameters", so it may not be immediately obvious that what you want here is co_argcount —but trust me, it is.

So:

def wrapped(*args):
    return function_(*(args[:function_.__code__.co_argcount]))

Depending on what you want to do with keyword arguments (which, remember, may match named positional-or-keyword or positional-only parameters!), you may want to do something with **kw and co_varnames as well.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM