1

In Python, I am using dataclass with the following class hierarchy:

from dataclasses import dataclass

@dataclass
class Foo:
  id: str

  @classmethod
  def fromRaw(cls, raw: dict[str, str]) -> 'Foo':
    return Foo(raw['id'])

@dataclass
class Bar(Foo):
  name: str

  @classmethod
  def fromRaw(cls, raw: dict[str, str]) -> 'Bar':
    return Bar(raw['id'], raw['name'])

However, it feels a bit double to instantiate all Foo members manually again in Bar::fromRaw().

Is there a better way to re-use the ancestor's alternate constructor for example?

1 Answer 1

4

This'll do:

@classmethod
def from_raw(cls, raw: dict[str, str]):
    return cls(**raw)
  1. Use cls instead of hardcoding Foo, so the method instantiates child classes when called on child classes.
  2. Use dict unpacking to turn a dict directly into named arguments, so you don't have to spell them out one by one.

Of course, now that you have this, you may see that you don't really need this method at all, and you can just do Bar(**data) anywhere, instead of Bar.from_raw(data).

If data may contain keys the dataclass doesn't, you'd need to do a little introspection on the fields of the dataclass, and filter out nonexistent fields:

from dataclasses import fields
from typing import Self

...

@classmethod
def from_raw(cls, raw: dict[str, str]) -> Self:
    keys = {f.name for f in fields(cls)}
    return cls(**{k: v for k, v in raw.items() if k in keys})
Sign up to request clarification or add additional context in comments.

2 Comments

Use typing.Self for the return annotation as well.
Yeah, was debating whether to include that detail or not; did so now.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.