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(CampingEquipment):
    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(equipment(**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).

Answer from Trey Hunner on Stack Overflow
Top answer
1 of 4
96

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(CampingEquipment):
    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(equipment(**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).

2 of 4
17

A field object is supposed to be used with =, like a default value, not : like an annotation.

Specifying init=False for a field means the caller can't pass in a value for it at all. init=False fields are supposed to be set in __post_init__, like this:

@dataclass
class Example:
    a: int
    b: int
    c: int = field(init=False)

    def __post_init__(self):
        self.c = self.a + self.b

print(Example(1, 2).c) # prints 3

If you want to make it optional for the caller to provide a value, you can set a default value. If the caller doesn't provide a value, the default will be used instead:

@dataclass
class Example:
    a: int
    b: int
    c: int = -1

print(Example(1, 2).c) # prints -1
print(Example(1, 2, 3).c) # prints 3
🌐
Python
docs.python.org › 3 › library › dataclasses.html
dataclasses — Data Classes
3 weeks ago - Therefore, do not use __slots__ to retrieve the field names of a dataclass. Use fields() instead. To be able to determine inherited slots, base class __slots__ may be any iterable, but not an iterator. weakref_slot: If true (the default is False), add a slot named “__weakref__”, which is required to make an instance weakref-able. It is an error to specify weakref_slot=True without also specifying slots=True. Added in version 3.11. fields may optionally specify a default value, using normal Python syntax:
Discussions

"Arguments missing" for optional arguments in dataclass
Python version (& distribution if applicable, e.g. Anaconda): 3.7.8 · Creating an instance of a dataclass without arguments for optional fields will show no error More on github.com
🌐 github.com
4
January 28, 2021
python - How to create an optional field in a dataclass that is inherited? - Stack Overflow
The short version of that post is that in a function signature (including the dataclass-generated __init__ method), obligatory arguments (like NamedEvent's name) can not follow after arguments with default values (which are necessary to define the behavior of Event's updated_at) - a child's ... More on stackoverflow.com
🌐 stackoverflow.com
June 27, 2023
Mypy does not infer that a dataclass field is optional when None is assigned
Bug Report It's documented that a dataclass field can be declared as optional without Optional or | None by simply assigning None as a default value. However, mypy fails to infer this. To Repro... More on github.com
🌐 github.com
9
October 22, 2022
python - Can I have an optional parameter in dataclasses that is omitted when transformed to dict? - Stack Overflow
I wish to perform static type checking (pylance in vscode) on some dictionaries. The "tricky" part is the I want some of the parameters to be optional and not show up at all in the dictio... More on stackoverflow.com
🌐 stackoverflow.com
🌐
Reddit
reddit.com › r/learnpython › dataclass required field, but value can be nullable?
r/learnpython on Reddit: Dataclass required field, but value can be nullable?
February 21, 2024 -

I am testing the contents of some messages, and I have a bit of a conundrum. I have a spec for the messages, which includes a number of required and optional fields (the spec is AsyncAPI 2.5.0, if that helps). The spec gives a number of fields that are explicitly required, but are allowed to have a value of null.

I'm using marshmallow-dataclass to build the schema:

from typing import ClassVar, Type

from marshmallow import Schema
from marshmallow_dataclass import dataclass


@dataclass
class RequiredButNullable:
    Schema: ClassVar[Type[Schema]]
    required_field: str  # Required, cannot be null
    required_but_nullable: int  # this field must be present, but can be null
    some_other_field: float | None  # optional field, can be missing

If I load a valid message with required_but_nullable set to null, it throws a validation error, but if I change the typing to int | None, it allows an invalid message that is missing required_but_nullable.

Is there a way to define a custom type, e.g. Nullable[int], or do I need to add some form of validation function?

🌐
how.wtf
how.wtf › python-dataclasses-with-optional-fields.html
Python dataclasses with optional fields | how.wtf
February 19, 2023 - 1from dataclasses import dataclass, field, InitVar 2from typing import Optional 3 4@dataclass 5class Movie: 6 title: str 7 hidden: bool = field(init=False) 8 is_published: InitVar[Optional[bool]] = True 9 10 def __post_init__(self, is_published): 11 self.hidden = not(is_published) 12 13print(Movie(title="title", is_published=False)) # Movie(title='title', hidden=True) 14print(Movie(title="title", is_published=True)) # Movie(title='title', hidden=False) 15print(Movie(title="title")) # Movie(title='title', hidden=False)
🌐
Pydantic
docs.pydantic.dev › latest › concepts › dataclasses
Dataclasses - Pydantic Validation
If you don't want to use Pydantic's BaseModel you can instead get the same data validation on standard dataclasses. Python 3.9 and abovePython 3.10 and above · from datetime import datetime from typing import Optional from pydantic.dataclasses import dataclass @dataclass class User: id: int name: str = 'John Doe' signup_ts: Optional[datetime] = None user = User(id='42', signup_ts='2032-06-21T12:00') print(user) """ User(id=42, name='John Doe', signup_ts=datetime.datetime(2032, 6, 21, 12, 0)) """ from datetime import datetime from pydantic.dataclasses import dataclass @dataclass class User: id: int name: str = 'John Doe' signup_ts: datetime | None = None user = User(id='42', signup_ts='2032-06-21T12:00') print(user) """ User(id=42, name='John Doe', signup_ts=datetime.datetime(2032, 6, 21, 12, 0)) """ Note ·
🌐
Python
typing.python.org › en › latest › spec › dataclasses.html
Dataclasses — typing documentation
default_factory is an optional parameter that provides a runtime callback that returns the default value for the field. If neither default nor default_factory are specified, the field is assumed to have no default value and must be provided a value when the class is instantiated. factory is ...
🌐
Python Morsels
pythonmorsels.com › customizing-dataclass-fields
Customizing dataclass fields - Python Morsels
October 23, 2024 - from dataclasses import dataclass @dataclass class Transfer: sender: str receiver: str amount: float memo: str = "" Now if we call this class without specifying the memo field, we'll see that an empty string is used: >>> transfer = Transfer("Lauren", "Nicole", 20) >>> transfer.memo '' What if we'd like an optional dataclass field that needs its default value to be computed?
Find elsewhere
🌐
GitHub
github.com › microsoft › pylance-release › issues › 893
"Arguments missing" for optional arguments in dataclass · Issue #893 · microsoft/pylance-release
January 28, 2021 - Python version (& distribution if applicable, e.g. Anaconda): 3.7.8 · Creating an instance of a dataclass without arguments for optional fields will show no error
Author   talarari
Top answer
1 of 1
26

The underlying problem that you have seems to be the same one that is described here. The short version of that post is that in a function signature (including the dataclass-generated __init__ method), obligatory arguments (like NamedEvent's name) can not follow after arguments with default values (which are necessary to define the behavior of Event's updated_at) - a child's fields will always follow after those of its parent.

So either you have no default values in your parent class (which doesn't work in this case) or all your child's fields need default values (which is annoying, and sometimes simply not feasible).

The post I linked above discusses some patterns that you can apply to solve your problem, but as a nicer alternative you can also use the third part party package pydantic which already solved this problem for you. A sample implementation could look like this:

import pydantic
from datetime import datetime


class Event(pydantic.BaseModel):
    id: str
    created_at: datetime = None
    updated_at: datetime = None

    @pydantic.validator('created_at', pre=True, always=True)
    def default_created(cls, v):
        return v or datetime.now()

    @pydantic.validator('updated_at', pre=True, always=True)
    def default_modified(cls, v, values):
        return v or values['created_at']


class NamedEvent(Event):
    name: str

The default-value specification through validators is a bit cumbersome, but overall it's a very useful package that fixes lots of the shortcomings that you run into when using dataclasses, plus some more.

Using the class definition, an instance of NamedEvent can be created like this:

>>> NamedEvent(id='1', name='foo')
NamedEvent(id='1', created_at=datetime.datetime(2020, 5, 2, 18, 50, 12, 902732), updated_at=datetime.datetime(2020, 5, 2, 18, 50, 12, 902732), name='foo')
🌐
GitHub
github.com › python › mypy › issues › 13934
Mypy does not infer that a dataclass field is optional when None is assigned · Issue #13934 · python/mypy
October 22, 2022 - Bug Report It's documented that a dataclass field can be declared as optional without Optional or | None by simply assigning None as a default value. However, mypy fails to infer this. To Reproduce import dataclasses as dc @dc.dataclass ...
Author   Kentzo
🌐
Reddit
reddit.com › r/learnpython › prevent optional attribute set to none from reportgeneraltypeissues
r/learnpython on Reddit: Prevent optional attribute set to None from reportGeneralTypeIssues
October 3, 2023 -

I'm working on a project in which a dataclass holds information about a calculation. The class can be instantiated with only a few arguments, and the remaining arguments are filled in as the calculation proceeds. In the simplified example below, a user can make a Bicycle instance with only a model number and the code will fill in the rest as needed.

from typing import Optional
from pathlib import Path
from dataclasses import dataclass

@dataclass 
class Bicycle: 
    '''Holds information about bikes''' 
    name: Optional[str] = None 
    model: Optional[str] = None 
    datasheet: Optional[Path] = None

def add_missing_data(bike: Bicycle) -> None: 
    '''Adds missing Bicycle information''' 
    if bike.datasheet is None:
        bike.datasheet = get_datasheet(model=bike.model)

def get_datasheet(model: str) -> Path: 
    return Path(f'./unsorted_datasheets/{model}.pdf')

Because Bicycle.model is set to None, Pylance gives me this error:

Argument of type "str | None" cannot be assigned to parameter "model" of type "str" in function "get_datasheet"

I can use type checking in the add_missing_data function to ensure that get_datasheet is not called if bike.model == None, but I'm unsure if I'm using dataclasses/Optional correctly. Is there a better solution than to add type checking in all the functions that modify a Bicycle instance?

🌐
Python.org
discuss.python.org › ideas
Would anyone use an optional dataclass kw_only __post_init__? - Ideas - Discussions on Python.org
July 16, 2023 - I’m relatively new to a lot of Python features, and found myself updating an codebase to use dataclasses’ InitVar. When looking into how to pass the args, I came across this old issue. TLDR: InitVars are passed to post_init by position, not keyword. For backwards compatibility reasons, it’s probably too late to change the default behavior to pass by keyword.
🌐
GitHub
github.com › samuelcolvin › pydantic › issues › 1265
Optional fields & defaults for BaseModel vs @dataclass · Issue #1265 · pydantic/pydantic
February 27, 2020 - class Foo(BaseModel): w: Optional[int] x: Optional[int] = None y: int = None z: int = 'bad' @dataclass class Bar: w: Optional[int] x: Optional[int] = None y: int = None z: int = 'bad' print(Foo()) print(Bar(None, z=-1)) print(Bar(None)) # output: ...
Author   gsakkis
🌐
DEV Community
dev.to › divshekhar › python-data-class-parameters-b0
Python Data Class Parameters - DEV Community
August 12, 2024 - This is the first optional parameter in the python data class and is set to True by default. This creates the initialization function for the class with the class attributes. ... In the above code we used dataclass decorator and thus just declared ...
🌐
Real Python
realpython.com › python-data-classes
Data Classes in Python (Guide) – Real Python
March 8, 2024 - Learn how a Python dataclass reduces boilerplate, adds type hints and defaults, supports ordering and frozen instances, and still plays well with inheritance.
🌐
Python
peps.python.org › pep-0557
PEP 557 – Data Classes | peps.python.org
fields may optionally specify a default value, using normal Python syntax: @dataclass class C: a: int # 'a' has no default value b: int = 0 # assign a default value for 'b'
🌐
DEV Community
dev.to › devasservice › python-trick-using-dataclasses-with-fielddefaultfactory-4159
Python Trick: Using dataclasses with field(default_factory=...) - DEV Community
August 30, 2024 - from dataclasses import dataclass, field from typing import List @dataclass class Student: name: str grades: List[int] = field(default_factory=list) # Use default_factory for mutable default # Create new Student instances student1 = Student(name="Alice") student2 = Student(name="Bob", grades=[90, 85]) # Modify student1's grades student1.grades.append(95) print(student1) # Output: Student(name='Alice', grades=[95]) print(student2) # Output: Student(name='Bob', grades=[90, 85]) # Output: # Student(name='Alice', grades=[95]) # Student(name='Bob', grades=[90, 85])
🌐
InfoWorld
infoworld.com › home › software development › programming languages › python
How to use Python dataclasses | InfoWorld
October 22, 2025 - The dataclass decorator can take initialization options of its own. Most of the time, you won’t need to supply them, but they can come in handy for certain edge cases.