Dátové triedy

ako na dátové triedy s modulom udataclasses

Dátové triedy sú špeciálne triedy, ktoré slúžia na prenos dát. Obsahujú obyčajne iba členské premenné a minimum logiky. Často sa používajú na manažment konfigurácie alebo na správu serializovateľných údajov, ktoré sa napr. prenášajú prostredníctvom REST API alebo protokolom MQTT. Štandardná knižnica jazyka Python obsahuje priamu podporu v balíku dataclasses, ale najznámejšou knižnicou je rozhodne Pydantic.

Natívna podpora dátových tried v jazyku MicroPython však nie je a rovnako vlastnosti tohto jazyka obmedzujú ich plnohodnotné využívanie. Pre naše potreby budeme používať jednoduchú implementáciu v podobe modulu udataclasses.py, ktorý je inšpirovaný práve knižnicou Pydantic.

Vlastnosti modulu udataclasses.py

  • typová kontrola pri priradení hodnoty
    • typ určuje prvá priradená hodnota
    • pri ďalšom priradení sa kontroluje, či nová hodnota zodpovedá typu prvej priradenej hodnoty
  • slot-like správanie - nie je možné pridávať nové tribúty, ktoré nie sú definované v triede
  • dekorátor validator na tvorbu vlastných funkcií na validáciu členských premenných
    • validácia sa spustí automaticky pri pri priradení novej hodnoty
  • vytvorená trieda je iterovateľná
  • export dát do slovníka pomocou volania metódy .model_dump()
    • exportujú sa aj vnorené dátové triedy
  • reprezentácia objektu pomotou __repr__()
    • objekt sa vypíše v tvare ClassName(field=value, ...)

Vytvorenie triedy

class Address(Dataclass):
    city: str = None
    zip: str = None

class Person(Dataclass):
    name: str = None
    age: int = None
    address: Address = None

Vytvorenie inštancie

Inštanciu je možné vytvoriť priamo pomocou konštruktora:

p = Person(
    name="Alice",
    age=30,
    address=Address(city="Paris", zip='12345')
)

Rovnako je ju však možné vytvoriť rozbalením slovníka:

>>> d = {
  'address': {
    'zip': '54321',
    'city': 'Kosice'
  },
  'age': 23,
  'name': 'Juraj'
}

>>> p2 = Person(**d)
>>> print(p2)
Person(age=23,name='Juraj',address={'zip': 54321, 'city': 'Kosice'})

Čítanie hodnôt

>>> print(f'{p.name} is {p.age} years old and lives in {p.address.city}.')
Alice is 30 years old and lives in Paris.

Zápis hodnôt

Pri zápise hodnôt je dôležité, aby typ zapisovanej hodnoty zodpovedal typu členskej premennej. V opačnom prípade dôjde k výnimke ValueError:

# typ clenskej premennej p.age je int
>>> type(p.age)
<class 'int'>


# ak priradime hodnotu typu int, je vsetko v poriadku
>>> p.age = 32

# ak vsak priradime hodnotu ineho typu, dojde k vynimke
>>> p.age = 'twenty'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "models/udataclasses.py", line 67, in __setattr__
ValueError: Value "twenty" for attribute "age" is not of type "int".

Ak je však členská premenná po vytvorení typu None, jej typ sa určí pri priradení prvej hodnoty, ktorá nebude None:

>>> p3 = Person()
>>> p3.name = 'jano'
>>> p3.name = 23
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "models/udataclasses.py", line 67, in __setattr__
ValueError: Value "23" for attribute "name" is not of type "str".

Validácia hodnôt

V triede, ktorá je potomkom triedy Dataclass je možné vytvárať aj vlastné validátory členských premenných. Stačí vytvoriť inštančnú metódu s dekorátorom @validator():

class Person(Dataclass):
    name: str = None
    age: int = None
    address: Address = None

    @validator('age')
    def check_age(self, value):
        if value < 0:
            raise ValueError('Age is negative')