user1159290
user1159290

Reputation: 1003

Retrieving function arguments from *args and **kwargs

Let's say I have a function f() which I know accepts 1 argument, action, followed by a variable number of arguments.

Depending on the initial action value, the function expects a different set of following arguments, e.g. for action 1 we know we must have 3 extra arguments p1, p2 and p3. Depending on how the function was called, these args can be either in args or `kwargs.

How do I retrieve them?

def f(action, *args, **kwargs):
    if action==1:
    #we know that the function should have 3 supplementary arguments: p1, p2, p3
    #we want something like:
    #p1 = kwargs['p1'] OR args[0] OR <some default> (order matters?)
    #p1 = kwargs['p1'] OR args[0] OR <some default>
    #p1 = kwargs['p1'] OR args[0] OR <some default>

Note that:

f(1, 3, 4, 5)
f(1,p1=3, p2=4, p3=5)
f(1, 2, p2=4, p3=5)

will place the different p1, p2, p3 parameters in either args or kwargs. I could try with nested try/except statements, like:

try:
    p1=kwargs['p1']
except:
    try:
        p1=args[0]
    except:
        p1=default

but it does not feel right.

Upvotes: 1

Views: 1675

Answers (3)

Iliya
Iliya

Reputation: 552

You can try with, using OrderedDict:

from collections import OrderedDict


def f(action, *args, **kwargs):
    params = OrderedDict({
        'p1': 'default',
        'p2': 'default',
        'p3': 'default',
    })

    if action == 1:
        for index, param in enumerate(params.keys()):
            if index < len(args):
                params[param] = kwargs[param] if param in kwargs else args[index]
            else:
                params[param] = kwargs[param] if param in kwargs else params[param]

        # Do something with `params`.


f(1, 3, 4, 5)
f(1, p1=3, p2=4, p3=5)
f(1, 2, p2=4, p3=5)

If you need more parameters, just add them to the ordered dictionary.

Upvotes: 1

juanfe888
juanfe888

Reputation: 21

I feel you were quite close to the answer on your question in out of itself. You can do something like this:

def f(action, *args, **kwargs):
     if action==1:
     p1=kwargs.get('p1') or args[0] if len(args)>0 else 'default'
     ...

The order does matter. In this case if you the key arguments would take priority over positional ones.

Here are some input examples --> value of p1 in the function:

  • f(1,10)-->10
  • f(1,2,20)-->2
  • f(1,p1=10)-->10
  • f(1,10,p1=2)-->2
  • f(1,p3=20)-->'default'

Upvotes: 2

Dr. V
Dr. V

Reputation: 1914

I would call a sub-function with a defined argument list within f:

def f(action, *args, **kwargs):
    if action == 1:
        f_1(*args, **kwargs)
    ...

def f_1(p1, p2, p3):
    # now you know the args

Of course, this raises the question why you need the over-all function, but let's assume you have a reason for that ;). Else, you could also have a dictionary:

funcs = {1: f_1, 2: ....}

Then you can call the correct function with individual arguments as

funcs[action](p1, p2, p3)

Upvotes: 3

Related Questions