6

What exactly does the TYPE lambda do when used with defaultdict? I have this example and works fine even for int, list & lambda as argument:

d = defaultdict(int)
d['one'] = lambda x:x*x
d['one'](2)
4

d = defaultdict(list)
d['one'] = lambda x:x*x
d['one'](2)
4

d = defaultdict(lambda: None)
d['one'] = lambda x:x*x
d['one'](2)
4

I have the same result each time. So what is the main reason to initialize with lambda "default (lambda: None)"? Looks defaultdict dictionary does not care about the what TYPE of argument is passed in.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
rva
  • 119
  • 1
  • 1
  • 4

2 Answers2

7

Your example only makes sense when you access keys that are not explicitly added to the dictionary:

>>> d = defaultdict(int)
>>> d['one']
0
>>> d = defaultdict(list)
>>> d['one']
[]
>>> d = defaultdict(lambda: None)
>>> d['one'] is None
True

As you can see, using a default dict will give every key you try to access a default value. That default value is taken by calling the function you pass to the constructor. So passing int will set int() as the default value (which is 0); passing list will set list() as the default value (which is an empty list []); and passing lambda: None will set (lambda: None)() as the default value (which is None).

That’s what the default dictionary does. Nothing else.

The idea is that this way, you can set up defaults which you don’t need to manually set up the first time you want to access the key. So for example something like this:

d = {}
for item in some_source_for_items:
    if item['key'] not in d:
        d[item['key']] = []

    d[item['key']].append(item)

which just sets up a new empty list for every dictionary item when it is accessed, can be reduced to just this:

d = defaultdict(list)
for item in some_source_for_items:
    d[item['key']].append(item)

And the defaultdict will make sure to initialize the list correctly.

poke
  • 369,085
  • 72
  • 557
  • 602
4

You are not using the default value factory. You won't see a difference if all you do is assign to keys, rather than try and retrieve a key that isn't in the dictionary yet.

The default value factory (the first argument to defaultdict()) is not a type declaration. It is instead called whenever you try and access a key that isn't in the dictionary yet:

>>> from collections import defaultdict
>>> def demo_factory():
...     print('Called the factory for a missing key')
...     return 'Default value'
...
>>> d = defaultdict(demo_factory)
>>> list(d)  # list the keys
[]
>>> d['foo']
Called the factory for a missing key
'Default value'
>>> list(d)
['foo']
>>> d['foo']
'Default value'
>>> d['bar'] = 'spam'  # assignment is not the same thing
>>> list(d)
['foo', 'bar']
>>> d['bar']
'spam'    

Only the first time when I tried to access the key 'foo' was the factory called to produce a default value, which is then stored in the dictionary for future access.

So for each of your different examples, what varies between them is what default value will be produced for each. You never access this functionality, because you directly assigned to the 'one' key.

Had you accessed a non-existing key you'd have created an integer with value 0, an empty list or None, respectively.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343