Passing keyword arguments to a function when local variable names are same as function parameter names
Asked Answered
V

3

16

Is there a more succint way to write this?

f(a=a, b=b, c=c, d=d, e=e)

Background: I have a function with too many arguments

f(a, b, c, d, e):
    pass

I my program I have local variables that are named exactly same as the function parameters.

a, b, c, d, e = range(5)

I would like to call the function with keyword arguments. Since the variables are named the same, this is how the call would look.

g = f(a=a, b=b, c=c, d=d, e=e) # this can get very long

Of course, I can pass the aruguments using position instead of keywords like this

g = f(a, b, c, d, e) 

But a, b, c, d, e are just the names of variables in this example and it is easy to see the correct order. However unfortunately the variables in my program are named more complicatedly and there is no easily discernible natural order. So I really like to pass them by keyword to avoid any mistakes.

Vindicate answered 2/5, 2011 at 18:36 Comment(0)
N
8

You could do something like the following:

a, b, c, d, e = range(5)
arg_dict = lambda l: dict((k, globals()[k]) for k in l.split(', '))

arg_dict('a, b, c, d, e') => {'a': 0, 'c': 2, 'b': 1, 'e': 4, 'd': 3}, so you can call your function like this:

f(**arg_dict('a, b, c, d, e'))

This gives you the ability to specify exactly which variables you want to use. An alternative method for this that does not use globals() would be to use eval(), but it could make the use of the lambda potentially unsafe.

arg_dict = lambda l: dict(zip(l.split(', '), eval(l)))

If you would prefer to pass locals() in as an argument instead of using globals() in the lambda you can use the following:

arg_dict = lambda l, d=locals(): dict((k, d[k]) for k in l.split(', '))
f(**arg_dict('a, b, c, d, e'))

Thanks to senderle for the locals() suggestions.

Noyes answered 2/5, 2011 at 19:16 Comment(6)
The lambda has its own scope, so the local variables from its containing scope are not in locals() if it is used inside the lambda (but they will be in globals()).Noyes
Wunderbar! This is exactly what I was looking for. Thanks! I agree with @senderle that I could use locals() instead of globals() though.Vindicate
You can try it, but locals() won't work unless you pass it into the lambda as an argument. You can verify this with (lambda: locals())() and (lambda: globals())().Noyes
@Andrew, ah, of course -- that's quite correct. And indeed, my inclination would be to do just that -- pass it in as a argument to lambda.Tamarisk
@Andrew: Good point. I forgot that lambda has it's own scope. globals() it is then. Thanks for the explanation.Vindicate
@Andrew, could you pass locals in as a default arg? As in arg_dict = lambda l, d=locals(): dict((k, d[k]) for k in l.split(', '))?Tamarisk
P
4

locals() gives your local variables, so you could do

def somewhere():
  x = 3 # its a local
  f(**locals()) # same as f(x=3)

but you can surely see how very fragile this is.

Placement answered 2/5, 2011 at 18:59 Comment(1)
I was thinking about locals too. But it did not seem like the 'elegant' way to solve this. If all fails, may be I should stick with brute force! :)Vindicate
L
1

Why can't you use **kw here?

def foo(**kw):
    for k,v in kw.items():
       print k,v


foo(a=2)
foo(a=3, b=4)
foo(nonsene=True, blather=False)
Ligamentous answered 2/5, 2011 at 18:40 Comment(2)
I agree that it would be the natural choice. However, foo() is defined in an external module and I don't have control over how the function is defined.Vindicate
One alternative is to define bar(), as a wrapper for foo() and passes the arguments, but I was wondering if there was an easier way.Vindicate

© 2022 - 2024 — McMap. All rights reserved.