views:

610

answers:

4

I am writing a function and intended to use a dictionary key and its value as parameters. For example:

testDict={'x':2,'xS':4}

def newFunct(key,testDict['key']):
    newvalue=key+str(testDict['key'])
    return newValue

for key in testDict:
    newValue=newFunct(key,testDict[key])
    print newValue

I get a SyntaxError: invalid syntax when I hit the return button after typing the semicolon. I am guessing this is telling me I can't pass a dictionary value in that form. Presumably I can define a new variable

for key in testDict:
    xdixt=testDict[key]
    newValue=newFunct(key,xdixt)

and def the function using xdixt

but I am hoping there is some trick that I am missing. I Googled and found some reference to unpacking a dictionary but that didn't seem to work.

This Python stuff is really cool. My question was came about because I am trying to use some values I stored in a dictionary to create a new directory. Based on the material I read from Stephan's answer I wondered about how to generalize the information I learned. My directory name has five different pieces to it, each piece is the result of processing the values from myDict. The expression to create the directory name was getting too complicated and in my opinion too complicated to easily read. so I wondered if I could use the same approach to put the pieces into a list and then unpack them when it came time to create the directory name and it worked!

def createDirectory(myKey,**myDict): 
   pathList=[]
   pathList.append(myDict['subKey1'])
   pathList.append(myDict['subKey2'].lstrip('0'))
   pathList.append(myDict['subKey3'])
   etc  
   myPath=os.path.join(*myList)
   os.makedirs(myPath)
   return(myPath)
A: 

why don't you just do:

[k + str(v) for k, v in test_dict.iteritems()]

or for py3k:

[k + str(v) for k, v in test_dict.items()]

edit

def f(k, v):
    print(k, v)      # or do something much more complicated

for k, v in testDict.items():
    f(k, v)
SilentGhost
My function is much more complicated, notice I said for example. But thanks for the suggestion
PyNEwbie
the idea is the same
SilentGhost
I see that now, but I understood Stephan's answer much more quickly. But thanks for the help I can see your organization helps me with understanding some more about python
PyNEwbie
+4  A: 

Is this what you want?

def func(**kwargs):
    for key, value in kwargs.items():
        pass # Do something

func(**myDict) # Call func with the given dict as key/value parameters

(See the documentation for more about keyword arguments. The keys in myDict must be strings.)


Edit: you edited your question to include the following:

I think the ** notation in front of myDict instructs the function to expect a dictionary and to unpack the key (the first *) and the value, the second *

This is not correct. To understand what is happening, you must consider the following:

  • A function can have multiple formal parameters (a and b in this case):
def f1(a, b): pass
  • We can pass positional arguments to such function (like in most other languages):
f1(2, 3)
  • We can also pass keyword arguments:
f1(a=2, b=3)
  • We can also mix these, but the positional arguments must come first:
f1(2, b=3)
f1(a=2, 3)  # SyntaxError: non-keyword arg after keyword arg
  • There is a way to let a function accept an arbitrary number of positional arguments, which it can access as a tuple (args in this case):
def f2(*args): assert isinstance(args, tuple)
  • Now we can call f2 using separately specified arguments, or using a list whose contents first need to be unpacked, using a syntax similar to the notation used for *args:
f2(2, 3)
f2(*[2, 3])
  • Likewise, an arbitrary number of keyword arguments may be accepted:
def f3(**kwargs): pass
  • Note that f3 does not ask for a single argument of type dict. This is the kind of invocations it expects:
f3(a=2, b=3)
f3(**{'a': 2, 'b': 3})
  • All arguments to f3 must be named:
f3(2, 3)  # TypeError: f3() takes exactly 0 arguments (2 given)

Putting all of this together, and remembering that positional arguments must come first, we may have:

>>> def f4(a, b, *args, **kwargs):
...     print('%r, %r' % (args, kwargs))
... 
>>> f4(2, 3)
(), {}
>>> f4(2, 3, 4, 5)
(4, 5), {}
>>> f4(2, 3, x=4, y=5)
(), {'y': 5, 'x': 4}
>>> f4(2, 3, 4, 5, x=6, y=7)
(4, 5), {'y': 7, 'x': 6}
>>> f4(2, *[3, 4, 5], **{'x': 6, 'y': 7})
(4, 5), {'y': 7, 'x': 6}

Pay special attention to the following two errors:

>>> f4(2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f4() takes at least 2 arguments (1 given)
>>> f4(2, 3, a=4)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f4() got multiple values for keyword argument 'a'

The second error should help you explain this behavior:

>>> f4(**{'foo': 0, 'a': 2, 'b': 3, 'c': 4})
(), {'c': 4, 'foo': 0}
Stephan202
I think so. I will test this-thanks
PyNEwbie
This was very helpful thanks
PyNEwbie
You're welcome :)
Stephan202
seriously, though. he didn't have this problem, why there was a need to bring in **kwargs? the question and this particular answer are getting out of hands. OP needs to either ask new question or find numerous explanation on SO re * and **.
SilentGhost
I see , thanks for all this work, I clipped out the section I added as your explanation helps a lot more. Thanks
PyNEwbie
@SilentGhost: I may not have sufficiently understood the initial problem, but the answer did help him, and he learned something new. Good enough for me.
Stephan202
I actually needed the **kwargs. I wanted to ask the question as simply as I could. Since dictionaries can have dictionaries as values I assumed that any response would deal with all of the possibilities for a dictionary value passed to a function.
PyNEwbie
Stephan sorry for changing the accepted answer but after reading an answer to a different question given by SLott yesterday I understand / think Dan's answer is more on point. I did learn a lot from your answer and I appreciate the time you tool.
PyNEwbie
Sure, no problem :)
Stephan202
+2  A: 

Not sure why we are bringing in kwargs, this is much simpler than that. You said you're new to Python, I think you just need some Python fundamentals here.

def newFunct(key,testDict['key']):

Should be:

def newFunct(key, val):

There's no reason to use any special syntax on your second parameter to indicate that it's coming from a dictionary. It's just a parameter, you just happen to be passing the value from a dictionary item into it.

Further, once it's in the function, there's no reason to treat it in a special way either. At this point it's just a value. Which means that:

newvalue=key+str(testDict[key])

Can now just be:

newvalue=key+str(val)

So when you call it like this (as you did):

newValue=newFunct(key,testDict[key])

testDict[key] resolves to the value at 'key', which just becomes "val" in the function.


An alternate way, if you see it fit for whatever reason (and this is just something that's good to know), you could define the function thusly:

def newFunct(key, testDict):

Again, the second parameter is just a parameter, so we use the same syntax, but now we're expecting it to be a dict, so we should use it like one:

newvalue=key+str(testDict[key])

(Note: don't put quotes around 'key' in this case. We're referring to the variable called 'key', not a key called 'key'). When you call the function, it looks like this:

newValue=newFunct(key,testDict)

So unlike the first case where you're just passing one variable from the dictionary, you're passing a reference to the whole dictionary into the function this time.

Hope that helps.

After I read S.Lott's answer to another question I then realized your answer was simpler and more to the point. I did learn quite a bit from the earlier answer but this answer is actually more directly on point. Thanks for taking the time.
PyNEwbie
A: 

From your description is seems like testDict is some sort of global variable with respect to the function. If this is the case - why do you even need to pass it to the function?

Instead of your code:

testDict={'x':2,'xS':4}

def newFunct(key,testDict[key]):
    newvalue=key+str(testDict[key])
    return newValue

for key in testDict:
    newValue=newFunct(key,testDict[key])
    print newValue

You can simply use:

testDict={'x':2,'xS':4}

def newFunct(key):
    newvalue=key+str(testDict[key])
    return newValue

for key in testDict:
    newValue=newFunct(key)
    print newValue

If testDict is not meant to be in the global scope (which makes sense...), I would recommend simply passing a name for the dictionary and not "messing around" with variable length argument lists in this case:

testDict={'x':2,'xS':4}

def newFunct(key,dictionary):
    newvalue=key+str(dictionary[key])
    return newValue

for key in testDict:
    newValue=newFunct(key,testDict)
    print newValue
Roee Adler