Python Data Classes, a gyorsabb és tisztább kódokért

A hagyományos Python objektumok gyakran pazarlóak. A Python Data Classes használatával azonban drasztikusan csökkentheted a kód mennyiségét, miközben növeled a programod sebességét. A szabványos objektumok minden adatot egy belső szótárban tárolnak. Ez rugalmas, de nagy memóriaigénnyel jár. Ráadásul alapértelmezésben nem használhatók például szótár kulcsként sem. Ebben a cikkben hét konkrét lépésen keresztül optimalizáljuk az osztályaidat. Másold ki a kódokat és tedd hatékonyabbá a fejlesztést.

1. Fagyasztás a biztonságért

A frozen=True paraméterrel megváltoztathatatlanná (immutábilissá) teheted az objektumodat. Ez két előnnyel jár: biztonságosabb a kód, és az objektum „hash-elhetővé” válik.

Ez elengedhetetlen, ha az objektumot szótár (dictionary) kulcsként szeretnéd használni.

Íme a kód a gyorsítótárazáshoz:

from dataclasses import dataclass

@dataclass(frozen=True)
class CacheKey:
    user_id: int
    resource_type: str
    timestamp: int
    
cache = {}
# Most már használhatod kulcsként az objektumot
key = CacheKey(user_id=42, resource_type="profile", timestamp=1698345600)
cache[key] = {"data": "számítási_eredmény"}

Fagyasztás nélkül TypeError hibát kapnál a kulcs hozzáadásakor. Ez a minta megakadályozza a véletlen módosításokat is a program futása során.

2. Memória optimalizálás slotokkal

Több ezer objektum létrehozásakor a memóriaigény gyorsan megnő. A hagyományos __dict__ tárolás rengeteg helyet foglal.

A slots=True paraméterrel utasítod a Pythont, hogy szótár helyett fix méretű tömböt használjon. Ez jelentős memóriamegtakarítást és gyorsabb attribútum-elérést eredményez.

from dataclasses import dataclass

@dataclass(slots=True)
class Measurement:
    sensor_id: int
    temperature: float
    humidity: float

A kompromisszum annyi, hogy futásidőben nem adhatsz hozzá új mezőket az objektumhoz.

3. Egyenlőségvizsgálat testreszabása

Gyakran előfordul, hogy két objektumot azonosnak tekintünk, még ha a metaadataik el is térnek. Például egy felhasználó azonosítója ugyanaz, de a belépési ideje más.

A field(compare=False) beállítással kizárhatsz mezőket az összehasonlításból.

from dataclasses import dataclass, field
from datetime import datetime

@dataclass
class User:
    user_id: int
    email: str
    # A belépési idő nem számít az egyenlőségnél
    last_login: datetime = field(compare=False)
    login_count: int = field(compare=False, default=0)

user1 = User(1, "alice@example.com", datetime.now(), 5)
user2 = User(1, "alice@example.com", datetime.now(), 10)

print(user1 == user2) # True lesz, mert az ID és email egyezik

Ez megakadályozza a hibás egyezésvizsgálatokat a logikailag azonos entitásoknál.

4. Mutábilis alapértelmezések kezelése

A Python egyik klasszikus hibalehetősége a változtatható alapértelmezett értékek (pl. üres lista) használata. Ha így teszel, minden példány ugyanazon a listán fog osztozni.

Python Data Classes erre kínálja a default_factory megoldást.

from dataclasses import dataclass, field

@dataclass
class ShoppingCart:
    user_id: int
    # Minden példány új, üres listát kap
    items: list[str] = field(default_factory=list)
    metadata: dict = field(default_factory=dict)

cart1 = ShoppingCart(user_id=1)
cart2 = ShoppingCart(user_id=2)

cart1.items.append("laptop")
print(cart2.items) # Üres marad, helyesen

Ez a módszer listák, szótárak és halmazok esetén is kötelező a tiszta kódhoz.

5. Adatvalidáció és számított mezők

Az automatikusan generált __init__ metódus kényelmes, de néha ellenőrizni kell a bemenetet. Erre való a __post_init__ horog.

Ez a metódus közvetlenül az inicializálás után fut le. Használhatod mezők kiszámítására vagy hibák dobására.

from dataclasses import dataclass, field

@dataclass
class Rectangle:
    width: float
    height: float
    # Ez a mező nem kerül be az init paraméterek közé
    area: float = field(init=False)
    
    def __post_init__(self):
        self.area = self.width * self.height
        if self.width <= 0 or self.height <= 0:
            raise ValueError("A méreteknek pozitívnak kell lenniük!")

rect = Rectangle(5.0, 3.0)
print(rect.area) # 15.0

6. Automatikus rendezés

Szeretnéd sorba rendezni az objektumaidat külön kód írása nélkül? Az order=True paraméterrel a Python legenerálja az összehasonlító metódusokat (<, >, <=, >=).

A rendezés a mezők sorrendje alapján történik (balról jobbra).

from dataclasses import dataclass

@dataclass(order=True)
class Task:
    priority: int # Ez alapján rendez először
    name: str
    
tasks = [
    Task(priority=3, name="Alacsony prioritás"),
    Task(priority=1, name="Kritikus hiba"),
    Task(priority=2, name="Új funkció")
]

for task in sorted(tasks):
    print(f"{task.priority}: {task.name}")

Kimenet:
1: Kritikus hiba
2: Új funkció
3: Alacsony prioritás

7. Inicializáló változók (InitVar)

Néha olyan adatot kell átadnod a konstruktornak, amit nem akarsz az objektumban tárolni. Erre szolgál az InitVar.

Példánkban egy SSL kapcsolót adunk át, ami befolyásolja a kapcsolat stringet, de maga a kapcsoló nem tárolódik el.

from dataclasses import dataclass, field, InitVar

@dataclass
class DatabaseConnection:
    host: str
    port: int
    # Csak az init fázisban létezik
    ssl: InitVar[bool] = True
    connection_string: str = field(init=False)
    
    def __post_init__(self, ssl: bool):
        protocol = "https" if ssl else "http"
        self.connection_string = f"{protocol}://{self.host}:{self.port}"

conn = DatabaseConnection("localhost", 5432, ssl=True)
print(conn.connection_string)
print(hasattr(conn, 'ssl')) # False, nem tároltuk el

Mikor ne használd?

Bár a Python Data Classes hatékony, nem mindenre megoldás. Kerüld a használatát, ha:

  • Bonyolult öröklődési hierarchiát építesz.
  • Az osztályod elsősorban viselkedést (metódusokat) tartalmaz, nem adatot.
  • Szigorú validációra vagy szerializációra van szükséged (ilyenkor a Pydantic jobb választás).

Az itt bemutatott technikákkal a kódod nemcsak rövidebb, de gyorsabb és megbízhatóbb is lesz.

Kérjük, ellenőrizd a mező formátumát, és próbáld újra.
Köszönjük, hogy feliratkoztál.

vagyunk.hu hírlevél

Hozzászólás

Az e-mail címet nem tesszük közzé. A kötelező mezőket * karakterrel jelöltük