Search code examples
pythonpython-dataclasses

Should dataclass use fields for attributes with only defaults?


When a python dataclass has a simple attribute that only needs a default value, it can be defined either of these ways.

from dataclasses import dataclass, field

@dataclass
class ExampleClass:
    x: int = 5  

@dataclass
class AnotherClass:
    x: int = field(default=5)  

I don't see any advantage of one or the other in terms of functionality, and so would go with the less verbose version. Of course field offers other bells and whistles, but I don't need them yet and could easily refactor to use field later.

Is there any advantage to using field for a simple default over just a type hint?


Solution

  • No, if all you need is a field with a default value and no other special behavior, assigning the value directly to the class variable is equivalent to a field with only a default parameter.

    x: int = field(default=5)
    x: int = 5
    

    In fact, Python goes way out of its way to make sure the two behave equivalently. From PEP 557,

    If the default value of a field is specified by a call to field(), then the class attribute for this field will be replaced by the specified default value. If no default is provided, then the class attribute will be deleted. The intent is that after the dataclass decorator runs, the class attributes will all contain the default values for the fields, just as if the default value itself were specified.

    So whether or not you assign a field, at runtime, the value of both of the x's above on the class (as opposed to an instance) will be the number 5.

    For the sake of completeness, the reasons you would want to call field rather than writing simply a type annotation include:

    • Providing a default_factory, which is a callable that runs every time an instance is constructed (i.e. x: list = [] will be the same list every time, whereas x: list = field(default_factory=list) will be a new list for each instance)
    • Removing the field from the parameters from consideration in some or all of the dataclass-generated functionality. You can remove the field from __init__ params, from the printed __repr__, from the comparison method, and from the generated __hash__.
    • Adding third party metadata. This doesn't affect dataclasses but can be used to share arbitrary information about a dataclass field with a third party library.

    If none of the above apply to you, stick to the simple syntax and don't call field.