20

I have some Python classes with class attributes and metaclass:

from abc import ABCMeta

class OldProduct(metaclass=ABCMeta):
    c_type: str
    c_brand: str

    def __init__(self, name:str):
        self.name = name

class OldLegoBox(OldProduct):
    c_type = "Toy"
    c_brand = "Lego"

    def __init__(self, name:str, price:float):
        self.name = name
        self.price = price

oldbox1 = OldLegoBox("Princess", 12.3)
print(oldbox1.c_brand)
oldbox2 = OldLegoBox("Knight", 42.3)
print(oldbox2.c_brand)

I would like to use dataclasses to improve the code: how to deal with the class properties c_type and c_brand?

I am thinking of the following which seems to fill my need:

from abc import ABCMeta
from dataclasses import dataclass, field


@dataclass
class Product(metaclass=ABCMeta):
    c_type: str
    c_brand: str
    name: str


@dataclass
class LegoBox(Product):
    name: str
    price: float
    c_type: str = field(default="Toy", init=False)
    c_brand: str = field(default="Lego", init=False)


box1 = LegoBox("Princess", 12.3)
print(box1)
box1.price = 1000
box1.name = "toto"
box1.c_brand = "some brand"
print(box1)
box2 = LegoBox("Knight", 42.3)
print(box2)

Is there a more appropriate approach?

Jean-Francois T.
  • 11,549
  • 7
  • 68
  • 107

1 Answers1

40

Dataclass class variables should be annotated with typing.ClassVar

@dataclass
class Product(metaclass=ABCMeta):
    c_type: ClassVar[str]
    c_brand: ClassVar[str]
    name: str

@dataclass
class LegoBox(Product):
    c_type: ClassVar[str] = "Toy"
    c_brand: ClassVar[str] = "Lego"
    price: float

Using abstract classes doesn't actually get you anything here, as far as I can tell, because there are no abstract methods. You can still create instances of Product, but they will not have c_type or c_brand attributes.

Patrick Haugh
  • 59,226
  • 13
  • 88
  • 96
  • Thanks. Yes, this example doesn't not highlight the interest of metaclasses. I wanted to do a small extract from the code we use. – Jean-Francois T. Sep 18 '19 at 07:16
  • 3
    somewhat surprised to find something in the `typing` module having a runtime effect – joel Jul 30 '20 at 20:18
  • 2
    It's more that the `dataclass` decorator inspects the type hints and uses them to generate code. The `ClassVar` isn't actually doing anything, but rather the code consuming it knows what to do when it sees a `ClassVar`. – Patrick Haugh Aug 01 '20 at 16:17