A Python data class is a regular Python class that has the @dataclass decorator. It is specifically created to hold data (from python.land).
Dataclasses reduce the boilerplate code when creating a Python class. As an example, below are 2 Python classes: the first is written with Python's standard class syntax, and the second is the simplified dataclass:
fromdataclassesimportdataclass## Standard Python classclassUser:def__init__(self,name:str,age:int,enabled:bool):self.name=nameself.age=ageself.enabled=enabled## Python dataclass@dataclassclassUser:user:strage:intenabled:bool
With a regular Python class, you must write an __init__() method, define all your parameters, and assign the values to the self object. The dataclass removes the need for this __init__() method and simplifies writing the class.
This example is so simple, it's hard to see the benefits of using a dataclass over a regular class. Dataclasses are a great way to quickly write a "data container," i.e. if you're passing results back from a function:
Instead of returning a dict, returning a dataclass allows for accessing parameters using .dot.notation, like function_results.original_value, instead of function_results["original_value"].
Dataclass Mixins
A mixin class is a pre-defined class you define with certain properties/methods, where any class inheriting from this class will have access to those methods.
For example, the DictMixin dataclass below adds a method .as_dict() to any dataclass that inherits from DictMixin.
DictMixin class
Adds a .as_dict() method to any dataclass inheriting from this class. This is an alternative to dataclasses.asdict(_dataclass_instance), but also not as flexible.
fromdataclassesimportdataclassfromtypingimportGeneric,TypeVar## Generic type for dataclass classesT=TypeVar("T")@dataclassclassDictMixin:"""Mixin class to add "as_dict()" method to classes. Equivalent to .__dict__. Adds a `.as_dict()` method to classes that inherit from this mixin. For example, to add `.as_dict()` method to a parent class, where all children inherit the .as_dict() function, declare parent as: # ``` py linenums="1" # @dataclass # class Parent(DictMixin): # ... # ``` # and call like: # ```py linenums="1" # p = Parent() # p_dict = p.as_dict() # ``` """defas_dict(self:Generic[T]):"""Return dict representation of a dataclass instance. Description: self (Generic[T]): Any class that inherits from `DictMixin` will automatically have a method `.as_dict()`. There are no extra params. Returns: A Python `dict` representation of a Python `dataclass` class. """try:returnself.__dict__.copy()exceptExceptionasexc:raiseException(f"Unhandled exception converting class instance to dict. Details: {exc}")## Demo inheriting from DictMixin@dataclassclassExampleDictClass(DictMixin):x:inty:intz:strexample:ExampleDictclass=ExampleDictClass(x=1,y=2,z="Hello, world!")example_dict:dict=example.as_dict()print(example_dict)# {"x": 1, "y": 2, "z": "Hello, world!"}
JSONEncodeMixin
Inherit from the json.JSONEncoder class to allow returning a DataClass as a JSON encode-able dict.
Python dataclasses do not have built-in validation, like a Pydantic class. You can still use type hints to define variables, like name: str = None, but it has no actual effect on the dataclass.
You can use the __post_init__(self) method of a dataclass to perform data validation. A few examples below:
fromdataclassesimportdataclassimporttypingastfrompathlibimportPathdefvalidate_path(p:t.Union[str,Path]=None)->Path:assertp,ValueError("Missing an input path to validate.")assertisinstance(p,str)orisinstance(p,Path),TypeError(f"p must be a str or Path. Got type: ({type(p)})")p:Path=Path(f"{p}")if"~"inf"{p}":p=p.expanduser()returnp@dataclassclassComputerDirectory:## Use | None in the annotation to denote an optional valuedir_name:str|None=Nonedir_path:t.Union[str,Path]=Nonedef__post_init__(self):ifself.dir_nameisNone:## self.dir_name is allowed to be Nonepasselse:ifnotisinstance(self.dir_name,str):raiseTypeError("dir_name should be a string.")ifself.dir_pathisNone:raiseValueError("Missing required parameter: dir_path")else:## Validate self.dir_path with the validate_path() functionself.dir_path=validate_path(p=self.dir_path)