Search code examples
python-3.xdefaultdictpython-dataclasses

Trouble creating DefaultDict in Dataclass


I am having trouble setting up a simple dataclass using a new defaultdict(dict).

If I tell the factory to use 'dict' as below , instantiation fails with typerror collection.defaultdict object is not callable

from collections import defaultdict
from dataclasses import dataclass, field

@dataclass
class ResultSet:
    changed: bool = False
    mqttdata:  defaultdict(dict) = field(default_factory=defaultdict(dict)) # does not work!

It does sorta work using field(default_factory=defaultdict) but then my code will fail later when it encounters missing keys - presumably because defaultdict was not set up for dict.

How do I properly set up a new defaultdict(dict) in a dataclass?


Solution

  • You have a few problems with the code and how you are using dataclasses currently:

    • Type generics in annotations need to be specified through square brackets [] generally, so default[dict] instead of defaultdict(dict) for example.

    • The default_factory argument to dataclasses.field() needs to be a no-arg callable which returns a new object with default values set. For example, assuming you have a nested dataclass Inner which specifies defaults for all fields, you could use default_factory=Inner to create a new Inner object each time the main dataclass is instantiated.

      Note that the default_factory argument is mainly useful for mutable types such as set, list, and dict, so that the same object isn't shared (and potentially mutated) between dataclass instances.


    Putting it all together, here is the working code which sets a default value for a field of type defaultdict[dict]:

    from collections import defaultdict
    from dataclasses import dataclass, field
    
    
    @dataclass
    class ResultSet:
        changed: bool = False
        mqttdata: defaultdict[dict] = field(default_factory=lambda: defaultdict(dict))  # works!
    
    
    print(ResultSet())
    

    In Python versions earlier than 3.9, which is when PEP 585 was introduced, you'll need to add the following import at the top so that any type annotations are lazy-evaluated:

    from __future__ import annotations