Using the Python dataclass
decorator generates signatures with arguments in a particular order:
from dataclasses import dataclass
from inspect import signature
@dataclass
class Person:
age: int
name: str = 'John'
print(signature(Person))
Gives (age: int, name: str = 'John') -> None
.
Is there a way to capture the order of arguments given when Person is instantiated? That is: Person(name='Jack', age=10) -> ('name', 'age')
. I'm at a loss because writing an __init__
method on Person
defeats most reasons for using the dataclass
decorator. I don't want to lose the type hints you get when creating a Person
, but I need to serialize the instance to JSON with keys in the order used at initialization.
I think this is a good use case for writing your own __init__
, and I don't think that defeats the point of using a dataclass at all. You still get a nice __str__
, __repr__
, __eq__
, and (if frozen) __hash__
. You also get pattern matching for free.
A double-star kwargs
argument is a Python dictionary, and Python dictionaries remember the order in which fields are inserted. Consider something like this.
from __future__ import annotations
from dataclasses import dataclass, InitVar
from inspect import signature
from typing import Any
@dataclass(init=False)
class Person:
age: int
name: str = 'John'
original_ctor_args: InitVar[dict[str, Any]]
def __init__(self, **kwargs):
self.age = kwargs['age']
self.name = kwargs.get('name', 'John')
self.original_ctor_args = list(kwargs)
print(Person(age=10, name = 'Joe').original_ctor_args) # Prints ['age', 'name']
print(Person(name = 'Alice', age=15).original_ctor_args) # Prints ['name', 'age']
print(Person(age=20).original_ctor_args) # Prints ['age']