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)
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}
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.
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)
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
Related
How are "keyword arguments" different from regular arguments? Can't all arguments be passed as name=value instead of using positional syntax?
There are two related concepts, both called "keyword arguments".
On the calling side, which is what other commenters have mentioned, you have the ability to specify some function arguments by name. You have to mention them after all of the arguments without names (positional arguments), and there must be default values for any parameters which were not mentioned at all.
The other concept is on the function definition side: you can define a function that takes parameters by name -- and you don't even have to specify what those names are. These are pure keyword arguments, and can't be passed positionally. The syntax is
def my_function(arg1, arg2, **kwargs)
Any keyword arguments you pass into this function will be placed into a dictionary named kwargs. You can examine the keys of this dictionary at run-time, like this:
def my_function(**kwargs):
print str(kwargs)
my_function(a=12, b="abc")
{'a': 12, 'b': 'abc'}
There is one last language feature where the distinction is important. Consider the following function:
def foo(*positional, **keywords):
print "Positional:", positional
print "Keywords:", keywords
The *positional argument will store all of the positional arguments passed to foo(), with no limit to how many you can provide.
>>> foo('one', 'two', 'three')
Positional: ('one', 'two', 'three')
Keywords: {}
The **keywords argument will store any keyword arguments:
>>> foo(a='one', b='two', c='three')
Positional: ()
Keywords: {'a': 'one', 'c': 'three', 'b': 'two'}
And of course, you can use both at the same time:
>>> foo('one','two',c='three',d='four')
Positional: ('one', 'two')
Keywords: {'c': 'three', 'd': 'four'}
These features are rarely used, but occasionally they are very useful, and it's important to know which arguments are positional or keywords.
Using keyword arguments is the same thing as normal arguments except order doesn't matter. For example the two functions calls below are the same:
def foo(bar, baz):
pass
foo(1, 2)
foo(baz=2, bar=1)
Positional Arguments
They have no keywords before them. The order is important!
func(1,2,3, "foo")
Keyword Arguments
They have keywords in the front. They can be in any order!
func(foo="bar", baz=5, hello=123)
func(baz=5, foo="bar", hello=123)
You should also know that if you use default arguments and neglect to insert the keywords, then the order will then matter!
def func(foo=1, baz=2, hello=3): ...
func("bar", 5, 123)
There are two ways to assign argument values to function parameters, both are used.
By Position. Positional arguments do not have keywords and are assigned first.
By Keyword. Keyword arguments have keywords and are assigned second, after positional arguments.
Note that you have the option to use positional arguments.
If you don't use positional arguments, then -- yes -- everything you wrote turns out to be a keyword argument.
When you call a function you make a decision to use position or keyword or a mixture. You can choose to do all keywords if you want. Some of us do not make this choice and use positional arguments.
I'm surprised that no one seems to have pointed out that one can pass a dictionary of keyed argument parameters, that satisfy the formal parameters, like so.
>>> def func(a='a', b='b', c='c', **kwargs):
... print 'a:%s, b:%s, c:%s' % (a, b, c)
...
>>> func()
a:a, b:b, c:c
>>> func(**{'a' : 'z', 'b':'q', 'c':'v'})
a:z, b:q, c:v
>>>
Using Python 3 you can have both required and non-required keyword arguments:
Optional: (default value defined for param 'b')
def func1(a, *, b=42):
...
func1(value_for_a) # b is optional and will default to 42
Required (no default value defined for param 'b'):
def func2(a, *, b):
...
func2(value_for_a, b=21) # b is set to 21 by the function call
func2(value_for_a) # ERROR: missing 1 required keyword-only argument: 'b'`
This can help in cases where you have many similar arguments next to each other especially if they are of the same type, in that case I prefer using named arguments or I create a custom class if arguments belong together.
I'm surprised no one has mentioned the fact that you can mix positional and keyword arguments to do sneaky things like this using *args and **kwargs (from this site):
def test_var_kwargs(farg, **kwargs):
print "formal arg:", farg
for key in kwargs:
print "another keyword arg: %s: %s" % (key, kwargs[key])
This allows you to use arbitrary keyword arguments that may have keys you don't want to define upfront.
I was looking for an example that had default kwargs using type annotation:
def test_var_kwarg(a: str, b: str='B', c: str='', **kwargs) -> str:
return ' '.join([a, b, c, str(kwargs)])
example:
>>> print(test_var_kwarg('A', c='okay'))
A B okay {}
>>> d = {'f': 'F', 'g': 'G'}
>>> print(test_var_kwarg('a', c='c', b='b', **d))
a b c {'f': 'F', 'g': 'G'}
>>> print(test_var_kwarg('a', 'b', 'c'))
a b c {}
Just suplement/add a way for defining the default value of arguments that is not assigned in key words when calling the function:
def func(**keywargs):
if 'my_word' not in keywargs:
word = 'default_msg'
else:
word = keywargs['my_word']
return word
call this by:
print(func())
print(func(my_word='love'))
you'll get:
default_msg
love
read more about *args and **kwargs in python: https://www.digitalocean.com/community/tutorials/how-to-use-args-and-kwargs-in-python-3
what I want is, for a function:
def test(a = 1, b = 2):
return {'a':a, 'b':b}
for example test(a=2,b=1) gives {'a':2,'b':1}. I want to do this automatically (like packing all the parameter into a dictionay), because my function is under development and there will be a lot of more arguments added, so I don't want to add this everywhere, is there a clean way to do so?
The function's form must be test(a=1,b=2), I can't change this into test(**kwarg), because it will be a huge change to the existing code. I am asking is there a way to get the dict from the form of input like (a=1,b=2)
def test(a = 1, b= 2):
x = locals()
print(x)
test(a=5, b=6)
Please Use kwargs
def test(**kwargs):
return kwargs
print (test(a=1,b=2))
#OUTPUT:{'b': 2, 'a': 1}
I know what the meaning of an asterisk is in a function definition in Python.
I often, though, see asterisks for calls to functions with parameters like:
def foo(*args, **kwargs):
first_func(args, kwargs)
second_func(*args, **kwargs)
What is the difference between the first and the second function call?
Let args = [1,2,3]:
func(*args) == func(1,2,3) - variables are unpacked out of list (or any other sequence type) as parameters
func(args) == func([1,2,3]) - the list is passed
Let kwargs = dict(a=1,b=2,c=3):
func(kwargs) == func({'a':1, 'b':2, 'c':3}) - the dict is passed
func(*kwargs) == func(('a','b','c')) - tuple of the dict's keys (in random order)
func(**kwargs) == func(a=1,b=2,c=3) - (key, value) are unpacked out of the dict (or any other mapping type) as named parameters
The difference is how the arguments are passed into the called functions. When you use the *, the arguments are unpacked (if they're a list or tuple)—otherwise, they're simply passed in as is.
Here's an example of the difference:
>>> def add(a, b):
... print a + b
...
>>> add(*[2,3])
5
>>> add([2,3])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: add() takes exactly 2 arguments (1 given)
>>> add(4, 5)
9
When I prefixed the argument with *, it actually unpacked the list into two separate arguments, which were passed into add as a and b. Without it, it simply passed in the list as a single argument.
The same is the case for dictionaries and **, except they're passed in as named arguments rather than ordered arguments.
>>> def show_two_stars(first, second='second', third='third'):
... print "first: " + str(first)
... print "second: " + str(second)
... print "third: " + str(third)
>>> show_two_stars('a', 'b', 'c')
first: a
second: b
third: c
>>> show_two_stars(**{'second': 'hey', 'first': 'you'})
first: you
second: hey
third: third
>>> show_two_stars({'second': 'hey', 'first': 'you'})
first: {'second': 'hey', 'first': 'you'}
second: second
third: third
def fun1(*args):
""" This function accepts a non keyworded variable length argument as a parameter.
"""
print args
print len(args)
>>> a = []
>>> fun1(a)
([],)
1
# This clearly shows that, the empty list itself is passed as a first argument. Since *args now contains one empty list as its first argument, so the length is 1
>>> fun1(*a)
()
0
# Here the empty list is unwrapped (elements are brought out as separate variable length arguments) and passed to the function. Since there is no element inside, the length of *args is 0
>>>
I wrote the following function to return a subset of a dictionary but want to insist on having at least one key provided in the input arguments.
def getProperty(self,x, *key):
key = [k for k in key]
key.insert(0,x)
return {k:self.__properties[k] for k in key if k in self.__properties}
The code works.
I know the insert is not really necessary since dictionary elements aren't ordered. What I really wish I could do is to get rid of the first for-loop that creates a list by extracting elements from the multi-argument tuple.
Something like
key = [x] + [key]
but it does not work.
This should do what you need:
def getProperty(self,x, *key):
key = (x,)+key
return {k:self.__properties[k] for k in key if k in self.__properties}
If you want to insist on there being at least one argument then you should assert that the args tuple is not empty
>>> def get_property(*args):
... assert (args != ()), "There must be at least one argument"
... print(args)
...
>>> get_property('a')
('a',)
>>> get_property('a', 'b')
('a', 'b')
>>> get_property()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 2, in get_property
AssertionError: There must be at least one argument
>>>
There's no need, then, to do anything fancy to provide one separate argument when you call the method and you can catch the AssertionError in the calling statement if required.
Forgive me if this has been asked before. I did not know how to search for it.
I'm quite familiar with the following idiom:
def foo():
return [1,2,3]
[a,b,c] = foo()
(d,e,f) = foo()
wherein the values contained within the left hand side will be assigned based upon the values returned from the function on the right.
I also know you can do
def bar():
return {'a':1,'b':2,'c':3}
(one, two, three) = bar()
[four, five, six] = bar()
wherein the keys returned from the right hand side will be assigned to the containers on the left hand side.
However, I'm curious, is there a way to do the following in Python 2.6 or earlier:
{letterA:one, letterB:two, letterC:three} = bar()
and have it work in the same manner that it works for sequences to sequences? If not, why? Naively attempting to do this as I've written it will fail.
Dictionary items do not have an order, so while this works:
>>> def bar():
... return dict(a=1,b=2,c=3)
>>> bar()
{'a': 1, 'c': 3, 'b': 2}
>>> (lettera,one),(letterb,two),(letterc,three) = bar().items()
>>> lettera,one,letterb,two,letterc,three
('a', 1, 'c', 3, 'b', 2)
You can see that you can't necessarily predict how the variables will be assigned. You could use collections.OrderedDict in Python 3 to control this.
If you modify bar() to return a dict (as suggested by #mikerobi), you might want to still preserve keyed items that are in your existing dict. In this case, use update:
mydict = {}
mydict['existing_key'] = 100
def bar_that_says_dict():
return { 'new_key': 101 }
mydict.update(bar_that_says_dict())
print mydict
This should output a dict with both existing_key and new_key. If mydict had a key of new_key, then the update would overwrite it with the value returned from bar_that_says_dict.
No, if you can not change bar function, you could create a dict from the output pretty easily.
This is the most compact solution. But I would prefer to modify the bar function to return a dict.
dict(zip(['one', 'two', 'three'], bar()))