Search code examples
pythonpython-typingpython-dataclassespyright

Is it possible to have an inline-definition of a dataclass in Python?


Preliminary note: The first three examples provided all work. My question is: If I declare dataclasses that are only used once inside another dataclass, can I avoid the "proper" declaration of the nested dataclass in favour of an "inline" declaration? I want to use nested dataclasses because it makes attribute access in my IDE simpler, i.e. I want to be able to use dot notation like MyNestedDataclass. and my IDE will suggest group_1 and group_2.


I want to define a nested dict and make use of Dataclass's feature for dot-notation - mainly because my type checker then suggests the fields. This is the data structure I aim for:

my_nested_dict = {
    "group_1": {
        "variable_1a":10,
        "variable_1b":True,
    },
    "group_2": {
        "variable_2a":"foo",
    }
}

Currently, I am doing this:

@dataclass
class Group1:
    variable_1a: int = 10
    variable_1b: bool = True

@dataclass
class Group2:
    variable_2a: str = "foo"

@dataclass
class MyNestedDataclass:
    group_1: Group1
    group_2: Group2

I have tried this pattern, but it leads to my type checker not knowing which keys should be allowed in MyNestedDataclass.group_1 and MyNestedDataclass.group2, and me not being able to access it like MyNestedDataclass.group_1.variable_1a:

@dataclass
class MyNestedDataclass:
    group_1: dict[str, Any] = field(default_factory=lambda:{
        "variable_1a":10,
        "variable_1b":True,
    })
    group_2: field(default_factory=lambda:{
        "variable_2a":"foo",
    })

It seems very unlikely, but is there something like this:

@dataclass
class MyNestedDataclass:
    group_1: Group1 = field(default_factory=lambda: dataclass(
        variable_1a: int = 10
        variable_1b: bool = True
    ))

    group_2: Group2= field(default_factory=lambda: dataclass(
        variable_2a: str = "foo"
    ))

Solution

  • Is it possible to have an inline-definition of a dataclass in Python?

    Short answer: No, not at type-checking time.

    It is possible to create a class, and thus a dataclass, at runtime with type():

    >>> from dataclasses import dataclass
    >>> C = dataclass(type('C', (), {'__annotations__': {'a': int, 'b': str}, 'a': 0, 'b': 'lorem'}))
    >>> help(C.__init__)
    Help on function __init__ in module __main__:
    
    __init__(self, a: int = 0, b: str = 'lorem') -> None
        Initialize self.  See help(type(self)) for accurate signature.
    

    However, the specification doesn't require type checkers to support it. In fact, it only ever mentions the class-based syntax.

    A type checker (in your case, Pyright) might or might not choose to add this extra support. You can open a feature request, but such a request is likely to be rejected, in my experience.