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).