I am attempting to understand a piece of code that involves a bit of use of the **kwargs when a function gets called. After reading through the Pydocs and some other posts on Stackoverflow, I attempted to experiment with this syntax interactively and encountered some cases where I could not understand the behavior:
In [180]: d1 = {'a':'b', 'c':'d'}
In [181]: d2 = {'e':'f', 'g':'h', 'i':'j'}
In [182]: dict(d1, d2)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-182-49f16d5922b6> in <module>()
----> 1 dict(d1, d2)
TypeError: dict expected at most 1 arguments, got 2
In [183]: dict(d1, **d2)
Out[183]: {'a': 'b', 'c': 'd', 'e': 'f', 'g': 'h', 'i': 'j'}
In [184]: dict(d1, *d2)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-184-0d16a1c0b887> in <module>()
----> 1 dict(d1, *d2)
TypeError: dict expected at most 1 arguments, got 4
In [185]: dict(*d1,d2)
File "<ipython-input-185-d36e77e65d5e>", line 1
dict(*d1,d2)
SyntaxError: only named arguments may follow *expression
In [186]: dict(**d1,d2)
File "<ipython-input-186-fd4b93fcc3c9>", line 1
dict(**d1,d2)
^
SyntaxError: invalid syntax
In the code that I am currently studying, I see the syntax of In [183]
being used and from what currently I understand of **d2
when it is used as such is that it expands the dictionary object d2
in place into a tuple of that looks like (e=f, g=h, i=j)
so the statement for In [183]
could look like (I'm not sure which one, but I tired all possibilities I could think of and none worked which puzzles me):
In [192]: dict({'a':'b', 'c':'d'}, (e=f, g=h, i=j))
File "<ipython-input-192-792b64fb793a>", line 1
dict({'a':'b', 'c':'d'}, (e=f, g=h, i=j))
^
SyntaxError: invalid syntax
for this above, I thought the brackets enclosing e=f, g=h, i=j
might be the problem because this works:
In [191]: dict(a=5,l=6)
Out[191]: {'a': 5, 'l': 6}
but when I removed them, I get:
In [196]: dict({'a':'b', 'c':'d'}, e=f, g=h, i=j)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-196-854a7d483a98> in <module>()
----> 1 dict({'a':'b', 'c':'d'}, e=f, g=h, i=j)
NameError: name 'f' is not defined
and trying just the e=f, g=h, i=j
portion alone does not work:
In [197]: dict(e=f, g=h, i=j)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-197-b53d153dfd98> in <module>()
----> 1 dict(e=f, g=h, i=j)
NameError: name 'f' is not defined
and then I realized that putting the ''
around f, h and i solved the problem:
In [199]: dict(e='f', g='h', i='j')
Out[199]: {'e': 'f', 'g': 'h', 'i': 'j'}
So I guess what I found here was that the right hand side of the key=value pair does not need to be defined as a string or perhaps any object type for that matter, but why is this so?
and when I went back to the original issue, I had
In [200]: dict({'a':'b', 'c':'d'}, e='f', g='h', i='j')
Out[200]: {'a': 'b', 'c': 'd', 'e': 'f', 'g': 'h', 'i': 'j'}
And so it finally worked!
Sorry if I have been not clear on what my issues are here, so to just summarize, I will list out all my questions on this:
How is it that when I did
In [183]: dict(d1, **d2)
it worked and I did not get any errors relating to the number of arguments, whereas forIn [182]: dict(d1, d2)
andIn [184]: dict(d1, *d2)
I got errors telling me that I have put in more arguments todict
than what it accepts? How didd1, **d2
turn into a single argument?From the errors in doing
In [185]: dict(*d1,d2)
andIn [186]: dict(**d1,d2)
, it seems clear the having*
or**
to the first dictionary object does not work. Why can't this be done? For example, why doesn'tdict(**d1, d2)
do the same thing asdict(d1, **d2)
?Somewhat related to Q1 - from my understanding of what
**kwargs
does, it somehow expands the dictionary out into itskey = value
and when applied to me example, probably gave me something likeIn [200]: dict({'a':'b', 'c':'d'}, e='f', g='h', i='j')
as it ran correctly. But doesn't this look like there were 4 arguments? Why didn't this throw an error?My last question is why when using the
key = value
pair syntax as arguments intodict
, we do not need to enclose thekey
in any quotes, but only do it for thevalue
, despite the key itself being astring
object too as is evident in the outputs from the successfuldict
constructions?
Thank you all very much for your help, and I will appreciate any comments or suggestions here too that may not be directly related to my problems here. This behavior just seems so obscure to me, and I can't understand it!
The behavior isn't obscure at all if you read the docs (https://docs.python.org/2/library/stdtypes.html#typesmapping)
Remember that when you call
dict()
, you are calling a function. Functions have a defined number of parameters, and these parameters have a defined order.There are three definitions of
dict()
, namely:It's simply just how the language was designed. This should answer your questions in (1) and (2).
EDIT: For (3), you're right in thinking that it expands the key/value pairs out.
**kwargs
isn
number of named parameters, so when you passi=1, j=2, k=3
you are passing 3 named parameters. If a function is defined with**kwargs
as a param, that means it can receive0 to n
named parameters. There's a distinction between a normal param and a named param.For (4), this is just a compiler trick. It's there for convenience I guess, but in practice it is rarely used. I personally have yet to see the
key=value
convention used in the "real world". YMMV.