Search code examples
pythondictionarypython-dataclasses

expanding a dataclass of type list by an element


I have defined a dataclass which is a list and I want to expand it:

@dataclass
class ShoppingCart:
    item_id: list = None

@dataclass
class Purchase:
    items: dict = ShoppingCart

@dataclass
class Item:
    price: int = 0
    name: str = ""

so later on I put an item in the Shoppingcart:

if getattr(inst_of_Purchase, inst_of_ShoppingCart) == None: ##no item in cart
    setattr(inst_of_Purchase, inst_of_ShoppingCart, inst_of_Item)
else: ## add an item
    ***

What is the correct Syntax to append an item to get a structure like this:

Purchase_1: {ShoppingCart_1: [item_1, item_2 ...], 
             ShoppingCart_2: [item_123, item_2 ...]...}
    

Solution

  • Here's how I would declare the models, using forward references so it's a bit easier to see the nested structure (top-down instead of bottom-up). If you want to go with a bottom-up approach as you initially had, you can swap the order you define the dataclasses and switch List['ShoppingCart'] to just List[ShoppingCart] for example.

    from dataclasses import dataclass
    from typing import List
    
    
    @dataclass
    class Purchase:
        carts: List['ShoppingCart'] = None
    
    
    @dataclass
    class ShoppingCart:
        # If you want default value to be an empty list:
        #  item: List['Item'] = dataclasses.field(default_factory=list)
        items: List['Item'] = None
    
    
    @dataclass
    class Item:
        price: int = 0
        name: str = ""
    

    Then if we need to add items to a new purchase:

    my_purchase = Purchase()
    
    if my_purchase.carts is None:  # no item in cart
        item_1 = Item(name='first item')
        item_2 = Item(2, 'second item')
    
        my_cart = ShoppingCart(items=[item_1, item_2])
        my_purchase.carts = [my_cart]
    
    else: ## add an item
        ...
    
    print(my_purchase)
    # Purchase(carts=[ShoppingCart(items=[Item(price=0, name='first item'), Item(price=2, name='second item')])])
    

    Note that if you're on Python 3.9+ (I wasn't sure if you are) you can remove the typing.List import, and use the built in list, as standard collections now support subscripted types. So for example, taken from the above:

    carts: List['ShoppingCart'] = None
    

    would become just (without the typing import):

    carts: list['ShoppingCart'] = None