Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python dict setdefault, confused

Tags:

python

I was looking for an algorithm, and I can't figure out why the dict d has values in it and curr does not. I think it does not seem like anything is being done to dict d.

>>> def what(*words):
...     d = {}
...     print d
...     for word in words:
...     print 'word: ' + word
...         curr = d
...         for letter in word:
...             curr = curr.setdefault(letter, {})
...         curr = curr.setdefault('.', '.')
...     print d
...     print '?'
...     print curr
...     return 1
... 
>>> what('foo') 
{}
word: foo
{'f': {'o': {'o': {'.': '.'}}}}
?
.
1
like image 810
user584583 Avatar asked Mar 21 '13 18:03

user584583


People also ask

What is Setdefault in Python dictionary?

Python Dictionary setdefault() Method The setdefault() method returns the value of the item with the specified key. If the key does not exist, insert the key, with the specified value, see example below.

How is the function get () different from the Setdefault () for dictionary class?

The get the method allows you to avoid getting a KeyError when the key doesn't exist however setdefault the method allows set default values when the key doesn't exist.

Should I use dict () or {}?

With CPython 2.7, using dict() to create dictionaries takes up to 6 times longer and involves more memory allocation operations than the literal syntax. Use {} to create dictionaries, especially if you are pre-populating them, unless the literal syntax does not work for your case.

What is the use of dict () in Python?

Python dict() Function The dict() function creates a dictionary. A dictionary is a collection which is unordered, changeable and indexed.


2 Answers

d = dict() --> initializes an empty dictionary and binds it to the name d; so you have a dictionary object ({}) referenced by name d

Inside the outer for loop
curr = d --> binds another name curr to the same object. So, names (d and curr refer to the same object)

Inside the inner for loop
During the first iteration letter = 'f'

curr = curr.setdefault(letter, {})

There are 2 things that are happening in the above statement,

A) curr.setdefault(letter, {}) --> As per documentation:

"If key is in the dictionary, return its value. If not, insert key with a value of default and return default. default defaults to None.".

Since, the letter 'f' is not in the initial dictionary object it mutates the initial object to {'f':{}} and returns the value {}, which is not the initial dictionary object, but a new one that was created because of the setdefault statement. At this time both curr and d refer to the initial dictionary object which has since mutated to {'f':{}}.

B) Reassignment of the name curr to the return value mentioned above. Now, the names curr and d refer to different objects. d refers to the object {'f':{}}, while curr refers to an empty dictionary object, which is actually the value of d['f']. This is why the nesting happens in the original dictionary object, as we go through the loop.

like image 123
Amit Avatar answered Sep 28 '22 17:09

Amit


Read the documentation for dict.setdefault: it is like get but if the key wasn't present then it is also set:

>>> my_dict = {}
>>> my_dict.setdefault('some key', 'a value')
'a value'
>>> my_dict
{'some key': 'a value'}
>>> my_dict.get('some key2', 'a value2')
'a value2'
>>> my_dict
{'some key': 'a value'}

Modifying a little your example:

>>> def what(*words):
...     d = dict()
...     for word in words:
...             curr = d
...             for letter in word:
...                     curr = curr.setdefault(letter, {})
...             curr = curr.setdefault('.', '.')
...             print 'curr is now: %r while d is %r' % (curr, d)
... 
>>> what('foo')
curr is now: '.' while d is {'f': {'o': {'o': {'.': '.'}}}}

As you can see curr changes, because when calling setdefault it sometimes(in your example always) create a new dict and set it as value to curr, while d always refers to the original dict. As you can see it is modified after the loop, since it's value is {'f': {'o': {'o': {'.': '.'}}}} which is quite different from {}.

Probably your confusion is due to the fact that curr = curr.setdefault(letter, {}) always create a new and empty dict, which is then assigned to curr(and thus for every letter you add a nesting level to the original dict instead of overwriting the values).

See this:

>>> my_dict = {}
>>> curr = my_dict
>>> for letter in 'foo':
...     print 'my_dict is now %r. curr is now %r' % (my_dict, curr)
...     curr = curr.setdefault(letter, {})
... 
my_dict is now {}. curr is now {}
my_dict is now {'f': {}}. curr is now {}
my_dict is now {'f': {'o': {}}}. curr is now {}
>>> my_dict
{'f': {'o': {'o': {}}}}

As you can see for every level the my_dict has a new nesting level.

Maybe, but I'm just guessing, you wanted to obtain something like 'foo' -> {'f': {}, 'o': {}}, in which case you should do:

>>> my_dict = {}
>>> for letter in 'foo':
...     my_dict.setdefault(letter, {})
... 
>>> my_dict
{'o': {}, 'f': {}}
like image 44
Bakuriu Avatar answered Sep 28 '22 18:09

Bakuriu