Search code examples
pythonpython-dataclasses

Automatic custom constructor for python dataclass


I'm trying to create a custom constructor for my python dataclass that will ideally take in a dict (from request json data) and fill in the attributes of the dataclass.

Eg

@dataclass
class SoldItem:
    title: str
    purchase_price: float
    shipping_price: float
    order_data: datetime

def main():
    json = requests.get(URL).json()
    sold_item = SoldItem(json)

So I want SoldItem to have a method that saves the json data in the appropriate attributes of the dataclass instead of having to do SoldItem(title=json['title']...

I would also preferably have the class be able to recognise that the data being passed in is a dict and execute the from dict constructor.

I have done my best to look up possible solutions but have come up mostly empty.

Any help would be greatly appreciated.


Solution

  • For the simplest approach - with no additional libraries - I would personally go with a de-structuring approach via **kwargs.

    For example:

    >>> json = {'title': 'test', 'purchase_price': 1.2, 'shipping_price': 42, 'order_data': datetime.min}
    >>> SoldItem(**json)
    SoldItem(title='test', purchase_price=1.2, shipping_price=42, order_data=datetime.datetime(1, 1, 1, 0, 0))
    

    In case of a more involved use case, such as:

    • a nested dataclass structure
    • the input dict is the result of an API call
    • keys in the dict are not in snake_case
    • value for a key does not match annotated type for a dataclass field

    In such cases, I would suggest third-party tools that will automagically handle this data transform for you.

    For instance, the dataclass-wizard is a (de)serialization library I have come up with, for exactly this use case, i.e. when the input data might be coming from another source, such as the result of an API call or response.

    It can be installed with pip:

    pip install dataclass-wizard
    

    Usage:

    from dataclasses import dataclass
    from datetime import datetime
    
    from dataclass_wizard import JSONWizard
    
    
    @dataclass
    class SoldItem(JSONWizard):
        title: str
        purchase_price: float
        shipping_price: float
        order_data: datetime
    
    
    def main():
        from pprint import pprint
    
        # json = requests.get(URL).json()
        json = {'title': 'test',
                'purchasePrice': '1.23',
                'shipping-price': 42,
                'Order_Data': '2021-01-02T12:34:56Z'}
    
        # create a `SoldItem` instance from an input `dict`,
        # such as from an API response.
        sold_item = SoldItem.from_dict(json)
    
        pprint(sold_item)
    
    
    if __name__ == '__main__':
        main()
    

    Prints:

    SoldItem(title='test',
             purchase_price=1.23,
             shipping_price=42.0,
             order_data=datetime.datetime(2021, 1, 2, 12, 34, 56, tzinfo=datetime.timezone.utc))