I would add a class method to the data class to extract the necessary values from a dict
.
@dataclass
class Tenant:
name: str
tenant_id: int
subdomain: str
api_client: str
@classmethod
def from_dict(cls, tenant: dict, api_client: str):
return cls(tenant["name"],
tenant["id"],
tenant["subdomain"],
api_client)
t1 = Tenant("alice", 5, "bar", "client")
t2 = Tenant.from_dict({"name": "bob", "id": 6, "subdomain": "foo"},
"client")
I would take the same approach even if Tenant
were not a dataclass. An instance of Tenant
is only interested in the values to assign to its attributes, not how those values are packaged prior to the instance being created.
If you must preserve the existing API for Tenant
, you'll need to use an InitVar
and the __post_init__
method.
from dataclasses import dataclass, InitVar, field
@dataclass
class Tenant:
tenant: InitVar[dict]
name: str = field(init=False)
tenant_id: int = field(init=False)
subdomain: str = field(init=False)
api_client: str
# Approximate __init__ method generated
# def __init__(self, tenant, api_client):
# self.api_client = api_client
# self.__post_init__(tenant)
def __post_init__(self, tenant):
self.name = tenant["name"]
self.tenant_id = tenant["id"]
self.subdomain = tenant["subdomain"]
t = Tenant({"name": "bob", "id": 6, "subdomain": "foo"},
"client")
tenant
, as an InitVar
, is passed to __init__
and __post_init__
, but will not be used as an attribute for the other autogenerated methods. name
, tenant_id
, and subdomain
will not be accepted as arguments to __init__
, but will be used by the other autogenerated methods. You, however, are responsible for ensuring they are set correctly in __post_init__
.
A possible hybrid approach to define a "private" class, and make the name Tenant
refer to the class method.
def _from_dict(cls, tenant, api_client):
return cls(tenant["name"],
tenant["id"],
tenant["subdomain"],
api_client)
# Using make_dataclass just to make the class name
# 'Tenant' instead of '_Tenant'. You can use an
# ordinary class statement and patch _Teant.__name__
# instead.
_Tenant = dataclasses.make_dataclass(
'Tenant',
[('name', str),
('tenant_id', int),
('subdomain', str),
('api_client', str)],
namespace={'from_dict': classmethod(_from_dict)}
)
Tenant = _Tenant.from_dict