andykais
andykais

Reputation: 1074

How to include ClassVars in dataclass asdict() output?

I have dataclasses with static fields on them like so:

from typing import ClassVar
from dataclasses import dataclass, asdict

@dataclass
class X:
  static_field: ClassVar[str] = "static_value"
  normal_field: str

@dataclass
class Y:
  x: X

y = Y(x=X(normal_field="normal_value"))
asdict(y) # yields {'x': {'normal_field': 'normal_value'}}

How can I make static_field part of the output? The field static_field should never be set when instantiating the class.

Upvotes: 2

Views: 1201

Answers (2)

Ben Kovitz
Ben Kovitz

Reputation: 5030

This solution uses an undocumented feature, the __dataclass_fields__ attribute, but it works at least in Python 3.9:

from dataclasses import dataclass
from typing import Dict, Any, ClassVar

def asdict_with_classvars(x) -> Dict[str, Any]:
    '''Does not recurse (see dataclasses._asdict_inner() for how to do that
    right), and fails if x lacks a class variable declared in x's class
    definition.'''
    return dict(
        (name, getattr(x, name))
            for name in x.__dataclass_fields__
    )

@dataclass
class Blah:
    x: int
    y: ClassVar[str] = 'the classvar'

print(asdict_with_classvars(Blah(22)))

Output:

{'x': 22, 'y': 'the classvar'}

If the ClassVar is declared in an abstract class and not defined, it's possible for an instance to be created that doesn't have a value for the ClassVar. In that case, the call to getattr() will raise an AttributeError. But it's not too hard to handle that and otherwise tailor this to your own needs.

Upvotes: 3

Carcigenicate
Carcigenicate

Reputation: 45806

From the docs:

If a field is a ClassVar, it is excluded from consideration as a field and is ignored by the dataclass mechanisms. Such ClassVar pseudo-fields are not returned by the module-level fields() function.

So, I don't think that this is possible. You may need to rethink that part of the design.

Upvotes: 0

Related Questions