eecs
eecs

Reputation: 141

Python dictionary argument passing methods

I am trying to pass a dictionary as an argument, and see 2 approaches as seen below, what's makes it keep the dictionary value unchanged when passed with **

def dict_test1(**d):
    d['a'] = '10'

def dict_test2(d):
    d['a'] = '10'


d = {'a': '1'}

dict_test1(**d) 
print d

dict_test2(d) 
print d

Output:

{'a': '1'}

{'a': '10'}

Upvotes: 0

Views: 52

Answers (2)

abarnert
abarnert

Reputation: 365707

To understand what's going on, consider this case:

>>> def test1(**d):
...     print(d)
>>> test1(spam=10)
{'spam': 10}

Where did that {'spam': 10} come from? Simple: **d just packs up all keyword arguments that don't match any named parameters, no matter where they came from, into a new dict, and gives you that dict as d.


Now look at ** on the argument side instead:

>>> def test2(spam, eggs):
...     print(spam, eggs)
>>> d = {'eggs': 10, 'spam': 20}
>>> test2(**d)
20 10

So Python is expanding **d into a bunch of separate keyword arguments, which match up with parameters exactly the same way as actual separate keyword arguments.


Finally, on both sides:

>>> def test3(spam, eggs, **d):
...     print(spam, eggs)
...     print(d)
>>> d = {'spam': 10, 'cheese': 20}
>>> test3(eggs=30, **d)
10 30
{'cheese': 20}

So Python is expanding the **d argument out into two separate keyword arguments, putting them together with the normal keyword argument eggs=30, matching them to the parameters spam and eggs, and storing any leftovers in a new dict in the **d parameter.


This is all explained loosely in the tutorial, and more rigorously in the reference.

There's really nothing special about the fact that you had a **d parameter on the definition and a **d argument on the call; they don't have to correspond to each other in any way.

Some people consider it unfortunate that Python uses the same syntax for variable-argument parameters in the definition and argument unpacking in the call, and likewise the same syntax for default parameter values in the definition and keyword arguments in the call. But, while this is confusing the first time you run into it, once you understand, it's very easy to remember, and to read. And other languages that distinguish the two with syntax like ...args vs. seq... don't seem to be any less initially confusing.

Upvotes: 0

MegaIng
MegaIng

Reputation: 7886

It is simply that (**d) creates a new dict, while (d) keeps a reference to the old one:

def dict_test1(**d):
    print d is d_global # False

def dict_test2(d):
    print d is d_global # True


d_global = {'a': '1'}

dict_test1(**d_global ) 
dict_test2(d_global) 

Upvotes: 3

Related Questions