1

I have two dataclasses inside bookmodel, one inherited from the other:

@dataclass(frozen=True)
class BookOrder:
    category: str
    name: str
    color: str
    price: int
    volume: int

@dataclass(frozen=True)
class ClientOrder(BookOrder):
    client_id: str

Then in another .py file, I need to init a ClientOrder instance using BookOrder:

from book_model import BookOrder
# ...

@dataclass
class Client:
    id: str
    def place_book_order(self, book_order: BookOrder):
        # want to init a ClientOrder HERE!!!

Since BookOrder is NOT callable, I cannot pass self.id to it. I was wondering if there's an elegant way to achieve this?

Update

Guess what I am trying to ask is, is there any other way to initialize a ClientOrder using BookOrder other than the below method?

client_order=ClientOrder(book_order.categry, book_order.name, ..., self.client_id)
5
  • I'd probably just not use inheritance, and instead make a ClientOrder a class that has two members: a BookOrder instance and a client_id. They you can just pass both to the ClientOrder initializer to create a client order. Commented Oct 31, 2021 at 14:41
  • Can you elaborate on “BookOrder is not callable”? If I understand, classes, like functions, should be callable. Commented Oct 31, 2021 at 14:43
  • 1
    @rv.kvetch class BookOrder does not have input params like ClientOrder does right? So I guess what I mean by not callable is that we cannot pass a id/any other vars as parameters to add as attribute to BookOrder. Commented Oct 31, 2021 at 14:47
  • What’s to stop you from creating a method like place_client_order which could be used to create a ClientOrder object? Commented Oct 31, 2021 at 15:00
  • 1
    @rv.kvetch Idk but when I init using this format client_order=ClientOrder(book_order) I got TypeError: __init__() missing 5 required positional arguments: 'name', 'color', 'price', 'volume', and 'client_id'. Does this mean I have to initialize it by specfing each attributes like client_order=ClientOrder(book_order.categry, book_order.name, ..., self.client_id)? Commented Oct 31, 2021 at 15:24

1 Answer 1

1

One way to solve this is by not using inheritance, and instead declaring client_id as an optional attribute instead. Since the BookOrder dataclass is also frozen, we then need to call object.__setattr__ to modify the value of client_id, as mentioned in this post.

In your first module a.py:

from __future__ import annotations  # Added for compatibility with 3.7+

from dataclasses import dataclass


@dataclass(frozen=True)
class BookOrder:
    category: str
    name: str
    color: str
    price: int
    volume: int
    client_id: str | None = None

In second module b.py:

from book_model import BookOrder
# ...

@dataclass
class Client:
    id: str
    def place_book_order(self, book_order: BookOrder):
        # The following does not work, because BookOrder is a frozen
        # dataclass.
        # setattr(book_order, 'client_id', self.id)
        # Use object.__setattr__ as mentioned here:
        #  https://stackoverflow.com/a/59249252/10237506
        object.__setattr__(book_order, 'client_id', self.id)

        if book_order.client_id:
            print('Successfully set client_id attribute:',
                  repr(book_order.client_id))
        else:
            print('Was unable to set client_id attribute on frozen dataclass')


Client('abc123').place_book_order(BookOrder(*['a'] * 5))

Outputs:

Successfully set client_id attribute: 'abc123'

Of course, the easier way is to just not define it as frozen dataclass:

@dataclass
class BookOrder:
    category: str
    name: str
    color: str
    price: int
    volume: int
    client_id: str | None = None

And then the only change needed in b.py:

    ...

    def place_book_order(self, book_order: BookOrder):
        book_order.client_id = self.id
        ...
Sign up to request clarification or add additional context in comments.

2 Comments

I see. Thanks for the detailed answer! (Have to use frozen plus inherited ClientOrder class is pre-defined so that's why I'm so lost haha)
No problem! I get what you were trying to do and it also would've been possible to (dynamically) create a ClientOrder object using the fields from a BookOrder, but I think with this approach it is overall a bit simpler.

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.