Python kalboje pažangaus lygio dekoratoriai ir meta-programavimas

Pradėjo Gozge, Vas 01, 2026, 10:16 AM

« ankstesnis - sekantis »

Gozge

Python dekoratoriai ne tik paprastam logging ar laiko matavimui, bet yra viena iš stipriausių kalbos meta-programavimo savybių. Pažangaus lygio dekoratoriai naudojami klasės elgsenai keisti, duomenų tikrinimui, lazy evaluation taikymui ir framework panašioms struktūroms kurti. Šiame straipsnyje giliai nagrinėsime labiausiai naudojamus įmontuotus dekoratorius (@property, @classmethod, @staticmethod) ir nuo nulio sukursime galingą dekoratorių šeimą.

1. @property – Getter/Setter padarymas Pythonic
Vienas iš dažniausiai naudojamų dekoratorių. Laukai (attribute) elgiasi kaip metodai, bet naudojami kaip kintamieji.

class Produktas:
    def __init__(self, pavadinimas, kaina):
        self.pavadinimas = pavadinimas
        self._kaina = kaina  # protected (konvencija)

    @property
    def kaina(self):
        return self._kaina

    @kaina.setter
    def kaina(self, nauja_kaina):
        if nauja_kaina < 0:
            raise ValueError("Kaina negali būti neigiama!")
        self._kaina = nauja_kaina

    @kaina.deleter
    def kaina(self):
        print("Kaina ištrinta!")
        self._kaina = 0

telefonas = Produktas("iPhone 16", 45000)
print(telefonas.kaina)  # 45000 (ne kaip metodas, o kaip savybė)
telefonas.kaina = 50000  # setter dirba
# telefonas.kaina = -100  # ValueError mesti
del telefonas.kaina  # deleter dirba


2. @classmethod ir @staticmethod – Klasių ir statiniai metodai
  • @classmethod: Teikia prieigą prie klasės (cls). Dažnai naudojamas alternatyviems konstruktoriams.
  • @staticmethod: Nėra pririštas nei prie klasės, nei prie objekto. Elgiasi kaip pagalbinė funkcija.

class Data:
    def __init__(self, metai, menuo, diena):
        self.metai, self.menuo, self.diena = metai, menuo, diena

    @classmethod
    def is_siandien(cls):
        from datetime import date
        siandien = date.today()
        return cls(siandien.year, siandien.month, siandien.day)

    @staticmethod
    def ar_keliamieji_metai(metai):
        return (metai % 4 == 0 and metai % 100 != 0) or (metai % 400 == 0)

    def __str__(self):
        return f"{self.diena:02d}.{self.menuo:02d}.{self.metai}"

print(Data.is_siandien())  # classmethod su šiandien
print(Data.ar_keliamieji_metai(2024))  # staticmethod -> True

3. Realus pasaulio taikymas: Cache + Validation + Logging dekoratorių šeima
Sukurkime profesionalią dekoratorių rinkinį, sujungiantį kelias savybes:

from functools import wraps
import time
from typing import Callable, Any

def cache(ttl: int = 300):  # sekundėmis
    """Rezultatus talpina tam tikrą laiką"""
    def decorator(func: Callable) -> Callable:
        talpyklos_duomenys = {}
        @wraps(func)
        def wrapper(*args, **kwargs):
            key = (args, frozenset(kwargs.items()))
            if key in talpyklos_duomenys:
                rezultatas, laikas = talpyklos_duomenys[key]
                if time.time() - laikas < ttl:
                    print(f"Talpyklos pataikymas: {func.__name__}")
                    return rezultatas
            rezultatas = func(*args, **kwargs)
            talpyklos_duomenys[key] = (rezultatas, time.time())
            return rezultatas
        return wrapper
    return decorator

def validate(*validators):
    """Parametrų tikrinimo dekoratorius"""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for i, validator in enumerate(validators):
                if i < len(args):
                    validator(args[i])
            return func(*args, **kwargs)
        return wrapper
    return decorator

def log_execution(level: str = "INFO"):
    """Registruoja funkcijos iškvietimus"""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            print(f"[{level}] {func.__name__} iškvietas -> args={args}, kwargs={kwargs}")
            rezultatas = func(*args, **kwargs)
            print(f"[{level}] {func.__name__} baigtas -> rezultatas={rezultatas}")
            return rezultatas
        return wrapper
    return decorator

Naudojimo pavyzdys – viską sujungiant:

@log_execution("DEBUG")
@cache(ttl=60)
@validate(lambda x: isinstance(x, int) and x > 0)
def fibonacci(n: int) -> int:
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(10))  # Pirmas kartas skaičiuoja + log
print(fibonacci(10))  # Iš talpyklos grąžina
# fibonacci(-5)  # ValueError (validate mesti)

Santraukos sąrašas: Dekoratorių pasirinkimo vadovas
  • Dekoratoriaus tipas: @property Naudojimo tikslas: Valdyti laukus su kontroliuojama prieiga Pavyzdžio scenarijus: Duomenų tikrinimas, lazy skaičiavimas
  • Dekoratoriaus tipas: @classmethod Naudojimo tikslas: Alternatyvūs konstruktoriai Pavyzdžio scenarijus: from_string, from_json kaip factory
  • Dekoratoriaus tipas: @staticmethod Naudojimo tikslas: Klasės specifinės pagalbinės funkcijos Pavyzdžio scenarijus: Pagalbinės matematikos funkcijos
  • Dekoratoriaus tipas: Cache dekoratorius Naudojimo tikslas: Pagreitinti brangias operacijas Pavyzdžio scenarijus: API kvietimai, DB užklausos
  • Dekoratoriaus tipas: Validate dekoratorius Naudojimo tikslas: Tikrinti įėjimo parametrus Pavyzdžio scenarijus: API endpoint'ai, vartotojo įvestys