6

Suppose I have a function get_data which takes some number of keyword arguments. Is there some way I can do this

def get_data(arg1, **kwargs):
    print arg1, arg2, arg3, arg4

arg1 = 1
data['arg2'] = 2 
data['arg3'] = 3 
data['arg4'] = 4 

get_data(arg1, **data)

So the idea is to avoid typing the argument names in both function calling and function definition. I call the function with a dictionary as argument and the keys of the dictionary become local variables of function and their values are the dictionary values

I tried the above and got error saying global name 'arg2' is not defined. I understand I can change the locals() in the definition of get_data to get the desired behavior.

So my code would look like this

def get_data(arg1, kwargs):
    locals().update(kwargs)
    print arg1, arg2, arg3, arg4

arg1 = 1
data['arg2'] = 2 
data['arg3'] = 3 
data['arg4'] = 4 

get_data(arg1, data)

and it wouldn't work too. Also cant I achieve the behavior without using locals()?

lovesh
  • 5,235
  • 9
  • 62
  • 93
  • I suggest you check out this question http://stackoverflow.com/questions/3394835/args-and-kwargs – Morgan Wilde May 16 '13 at 12:05
  • 2
    Don't modify `locals`, it's not even guaranteed to work all the time: http://docs.python.org/2/library/functions.html#locals – jamylak May 16 '13 at 12:10
  • 1
    .. why would you even do that? If you know arg2, arg3, arg4 are always going to be passed, then you should specify them as individual arguments. If you don't, then even if you had the desired behaviour, as soon as you referenced say `arg4` when `data` didn't define `arg4`, it would raise a NameError. It wouldn't even print any of the data, it would just raise an error. – kampu May 16 '13 at 12:56
  • @MorganWilde I understand this concept but the idea is to avoid writing names in function definition and function calling – lovesh May 16 '13 at 13:46
  • 1
    lovesh, it's a bad concept to avoid names in function definition as it makes very hard to understand what arguments are required and what arguments have no effects. – Vladimir May 16 '13 at 13:56
  • @Vladimir But can it be done? – lovesh May 19 '13 at 16:32
  • lovesh, what holds you from using `det_data(arg1, arg2, arg3, arg4): ...` signature? – Vladimir May 20 '13 at 06:28
  • @Vladimir more keystrokes :) – lovesh May 20 '13 at 06:46

2 Answers2

11

**kwargs is a plain dictionary. Try this:

def get_data(arg1, **kwargs):
    print arg1, kwargs['arg2'], kwargs['arg3'], kwargs['arg4']

Also, check documentation on keyword arguments.

Vladimir
  • 9,913
  • 4
  • 26
  • 37
4

If we examine your example:

def get_data(arg1, **kwargs):
    print arg1, arg2, arg3, arg4

In your get_data functions's namespace, there is a variable named arg1, but there is no variable named arg2. so you can not reach a function or a variable that is not in your namespace.

In fact, in your namespace; there is a variable arg1 and a dictionary object called kwargs. using ** notation before the kwargs (name kwargs is not important in here, it can be something else too.) tells your python compiler that kwargs is a dictionary and all values in that dictionary will be evaluated as named parameters in your function definition.

Lets take a look at this example:

def myFunc(**kwargs):
    do something

myFunc(key1='mykey', key2=2)

when you call myFunc with named parameters key1 and key2, your function definition acts like

def myFunc(key1=None, key2=None):

but with an exception! since myFunc have no named parameters, compiler do not know how to handle them directly since you can pass any named parameter to your function.

So your function accept those named parameters within a dictionary like you call your function:

myFunc({key1:'mykey', key2:2})

so, your function definition get those parameters within a dictionary. **kwargs defines your dictionary in that point, which also tell compiler that any named parameters will be accepted (by the help of ** signs before the kwargs)

def myFunc(**kwargs):
    print kwargs

will print

{key1:'mykey', key2:2}

So in your example, you can use kwargs like a dictionary;

def myFunc(**kwargs):
    kwargs['arg2']
    kwargs.get('arg3')

I hope it is not complicated (:

Mp0int
  • 18,172
  • 15
  • 83
  • 114