6

So say that I have a dictionary with a default value of another dictionary

attributes = { 'first_name': None, 'last_name': None, 'calls': 0 }
accounts = defaultdict(lambda: attributes)

The problem is that the default dictionary that I pass into defaultdict (attributes) is passed as a reference. How can I pass it as a value? So that changing the values in one key doesn't change the values in other keys

For example -

accounts[1]['calls'] = accounts[1]['calls'] + 1
accounts[2]['calls'] = accounts[2]['calls'] + 1
print accounts[1]['calls'] # prints 2
print accounts[2]['calls'] # prints 2

I want each of them to print 1, since I only incremented their respective values for 'calls' once.

satnam
  • 1,457
  • 4
  • 23
  • 43

2 Answers2

9

Try:

accounts = defaultdict(attributes.copy)

Since Python 3.3 listss also have copy method so you can use it the same way as above with defaultdicts when you need a dict with a list as a default value.

warvariuc
  • 57,116
  • 41
  • 173
  • 227
  • 1
    Yep! The factory can be any function that returns the value you want to use for missing keys (as long as it can be called without arguments), `attributes.copy` is just such a function! – kindall Feb 07 '17 at 19:20
3

I really like warvariuc's solution. However, remember, you are not passing a dict into defaultdict... that would result in a TypeError, because that argument must be a callable. You could have just used a literal in a lambda. Or better yet, define a helper function:

>>> def attribute():
...     return { 'first_name': None, 'last_name': None, 'calls': 0 }
...
>>> accounts = defaultdict(attribute)
>>> accounts[1]['calls'] = accounts[1]['calls'] + 1
>>> accounts[2]['calls'] = accounts[2]['calls'] + 1
>>> print(accounts[1]['calls'])
1
>>> print(accounts[2]['calls'])
1
juanpa.arrivillaga
  • 88,713
  • 10
  • 131
  • 172