better way to pass information from namedtuple as args

Viewed 202

I want to give a function values from a namedtuple which shares the same name as the function signature, and if the name doesn't exist in signature, skip it.

import collections
row = collections.namedtuple('row', ['b', 'a', 'c', 'd'])
row1 = row(1,2,3,4)
def assign_rows(a, b, c):
    print(a, b, c)
assign_rows(*row1)

Returns error

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-32-c5dc816f0d5d> in <module>
----> 1 assign_rows(*row1)

TypeError: assign_rows() takes 3 positional arguments but 4 were given

There are two problems, most obviously the error above, but also I want the argument to respect the name of the namedtuple, so a=row1.a, b=row1.b, c=row1.c. Is there a neat way to do this?

1 Answers

You can pass the namedtuple to the function as keyword parameters by converting it to a dict and using the double star operator:

assign_rows(**row1._asdict())

That will still leave you with the problem that you have an extra parameter (d) in the tuple that isn't expected by the function, which will cause an error.

If you don't mind changing the function, add **kwargs to the end of the function signature. That will suppress the error:

def assign_rows(a, b, c, **kwargs):
    print(a, b, c)

That's not a clean solution if you're going to use the function elsewhere in your code, as it suppresses what might in some cases be a useful error message. In that case, you can match the namedtuple to the function signature with something like.

import inspect 

def namedtuple_to_func_args(ntuple, fn_def):
    params = inspect.signature(fn_def).parameters
    tuple_dict = ntuple._asdict()
    return {p : tuple_dict[p] for p in params.keys()}

And then use

import collections
row = collections.namedtuple('row', ['b', 'a', 'c', 'd'])
row1 = row(1,2,3,4)
def assign_rows(a, b, c):
    print(a, b, c)
args = namedtuple_to_func_args(row1, assign_rows)
assign_rows(**args)

There are other spins on this it might work neater as a decorator, depending on the surrounding logic. You should also consider what juanpa says in the comments about just using a=row1.a etc, as it's simple and performant, depending on how often you're going to use this pattern and how flexible it needs to be

Related