python dataclasses with optional attributes

It’s not possible to use a dataclass to make an attribute that sometimes exists and sometimes doesn’t because the generated __init__, __eq__, __repr__, etc hard-code which attributes they check.

However, it is possible to make a dataclass with an optional argument that uses a default value for an attribute (when it’s not provided).

from dataclasses import dataclass
from typing import Optional

@dataclass
class CampingEquipment:
    knife: bool
    fork: bool
    missing_flask_size: Optional[int] = None
    
kennys_stuff = {
    'knife':True,
    'fork': True
}

print(CampingEquipment(**kennys_stuff))

And it’s possible to make a dataclass with an argument that’s accepted to __init__ but isn’t an actual field.
So you could do something like this:

from dataclasses import dataclass, InitVar
from typing import Optional

@dataclass
class CampingEquipment:
    knife: bool
    fork: bool
    missing_flask_size: InitVar[Optional[int]] = None

    def __post_init__(self, missing_flask_size):
        if missing_flask_size is not None:
            self.missing_flask_size = missing_flask_size

If you really want classes to either to have that attribute present or not have it at all, you could subclass your dataclass and make a factory function that creates one class or the other based on whether that missing_flask_size attribute is present:

from dataclasses import dataclass

@dataclass
class CampingEquipment:
    knife: bool
    fork: bool


@dataclass
class CampingEquipmentWithFlask:
    missing_flask_size: int


def equipment(**fields):
    if 'missing_flask_size' in fields:
        return CampingEquipmentWithFlask(**fields)
    return CampingEquipment(**fields)
    

kennys_stuff = {
    'knife':True,
    'fork': True
}

print(CampingEquipment(**kennys_stuff))

If you really wanted to (I wouldn’t recommend it though), you could even customize the __new__ of CampingEquipment to return an instance of that special subclass when that missing_flask_size argument is given (though then you’d need to set init=False and make your own __init__ as well on that class).

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)