Question Details

No question body available.

Tags

python enums python-dataclasses

Answers (1)

Accepted Answer Available
Accepted Answer
September 8, 2025 Score: 3 Rep: 165,865 Quality: Expert Completeness: 80%

The standard case for a dataclass is where the caller is able to explicitly pass in all of the field values, and those field values are all public. A typical call for the object you show might look like

req = RandomRequest(state=State.INC, delay=5)

With this call pattern, the defaultfactory will never be invoked.

If you expect that the normal call path will be to pass no parameters, then a data class probably isn't right. An ordinary class will have less boilerplate to set up.

# not a dataclass
class RandomRequest:
  def init(self):
    self.state = random.choice(list(State.members.values()))
    self.delay = int(random.gauss(avgdelays[self.state]))

If the normal call path will pass all of the parameters, but you also need a way to create one with random values, then using a class method as a secondary constructor could also be a clean approach.

from dataclasses import dataclass
from typing import Self

@dataclass class RandomRequest: state: State delay: int

@classmethod def random(cls) -> Self: state = random.choice(list(State.members.values())) delay = int(random.gauss(avgdelays[state])) return cls(state=state, delay=delay)

If you wanted to make this random construction generic across a range of dataclasses, you could use dataclasses.fields() to introspect a dataclass class and find its fields. You probably wouldn't normally need this, unless you're building some sort of automatic fuzzing tool (and even then I'd look for a prebuilt solution first).


The last example you show seems like it could benefit from refactoring. Using a non-dataclass would certainly work here: you'd only have to write out the properties once in the init() method, and you could use a super() call to inherit the base class's initializers. The classmethod approach would be a little harder to adapt, but it'd be possible (maybe passing along a **kwargs of additional parameters).

The three groups of identical fields with numbered suffixes suggests some reorganization might be appropriate. It seems like you have a group of states, and then multiple corresponding groups of delays. So if you have a way to compute the individual random values

class State(StrEnum):
  INC = "incoming"
  OUT = "outgoing"
  SLEEP = "sleeping"

@classmethod def random(cls) -> Self: return random.choice(list(cls.members.values()))

def randomdelay(self) -> int: avgdelays = { State.INC: 10, State.OUT: 20, State.SLEEP: 100 } return int(random.gauss(avgdelays[self.state]))

class States: def init(self): self.ini = State.random() self.ani = State.random() self.req = State.random() self.delays: list[Delays] = [Delays(self) for in range(3)]

class Delays: def init(self, states: States): self.ini = states.ini.randomdelay() self.ani = states.ani.randomdelay() self.req = states.req.randomdelay()

states = States() print(states.delays[0].ini)

Other refactorings are possible too; maybe it's more natural for your setup to pair the state and delay together, for example. It doesn't need to all be in one object, though, and in the last setup I think the very flat data structure is making it more complex than it needs to be.