Dekorátory

# předpokládáme existenci dekorátoru @vypis_cas


def uvar_kafe():
    print("Vařím kafe")


@vypis_cas
def natoc_pivo():
    print("Točím pivo")


uvar_kafe()  # Vařím kafe
natoc_pivo()  # 2020-09-08 10:50:47: Točím pivo

Možnosti použití

# (podmínečné) nahrazení původní funkce jinou


@deaktivuj_pri_testu
def posli_penize(kolik, komu):
    print(f"Posílám {kolik} peněz {komu}")


@deaktivuj_pri_testu
def posli_sms(text, komu):
    print(f"Posílám SMS {text} na číslo {komu}")
# přidání chování před/za kód původní funkce


@timer
def stahni_data_z_databaze():
    print("Stahuji seznam knih")

@timer
def nahraj_na_youtube():
    print("Nahrávám video")
@zkontroluj_opravneni
def zmenit_heslo(kdo, pro_koho, heslo):
    print(f"{kdo} mění heslo pro uživatele"
          f"{pro_koho} na {heslo}")


@zkontroluj_opravneni
def nahrat_obrazek(kdo, pro_koho, obrazek):
    print(f"{kdo} nahrává obrázek {obrazek}"
          f"pro uživatele {pro_koho}")
# zapsání do nějaké evidence

import atexit


@atexit.register
def rozluc_se():
    print("Děkujeme za použití programu!")


@atexit.register
def odeslat_data_do_nsa():
    # print("Nic nevypisuj, nebo to poznají!")
    pass
app = Flask("moje-stranka")


@app.route("/")
def uvitaci_stranka():
    return "Ahoj, vítám vás na svém webu!"


@app.route("/tajna-stranka")
def tajna_stranka():
    return "Tajné heslo je: PythonRulez"
class DataSet:
    def __init__(self, sequence_of_numbers):
        self._data = sequence_of_numbers

    @functools.cached_property
    def stdev(self):
        return statistics.stdev(self._data)

    @functools.cached_property
    def variance(self):
        return statistics.variance(self._data)
@click.command()
@click.option("--pocet", default=1)
@click.argument("jmeno")
def pozdrav(pocet, jmeno):
    for x in range(pocet):
        print(f"Ahoj {jmeno}")


@click.command()
def rozluc_se():
    print(f"Měj se fajn")

Jak vytvořit dekorátor?

def vypis_cas(puvodni_fce):

    def nahradni_fce():
        print(datetime.now(), end=": ")
        return puvodni_fce()

    return nahradni_fce

Dekorátory jsou (typicky) funkce, které obalují jiné funkce, čímž mohou přidávat nebo měnit jejich chování.

 

Originální funkci dostávají jako parametr a místo ní vrací jinou, která uvnitř používá tu původní.

Jak vytvořit dekorátor?

... správně

import functools


def vypis_cas(puvodni_fce):

    @functools.wraps(puvodni_fce)
    def nahradni_fce(*args, **kwargs):
        print(datetime.now(), end=": ")
        return puvodni_fce(*args, **kwargs)

    return nahradni_fce

BONUS

Implementace použitých dekorátorů

testujeme = True


def deaktivuj_pri_testu(puvodni_fce):

    @functools.wraps(puvodni_fce)
    def nahradni_fce(*args, **kwargs):
        if testujeme:
            print(f"Funkce {puvodni_fce.__name__} "
                   "byla deaktivována kvůli "
                   "testovacímu režimu")
        else:
            return puvodni_fce(*args, **kwargs)

    return nahradni_fce
def timer(puvodni_fce):

    @functools.wraps(puvodni_fce)
    def nahradni_fce(*args, **kwargs):
        start = time.perf_counter_ns()
        vysledek = puvodni_fce(*args, **kwargs)
        konec = time.perf_counter_ns()
        print(f"Funkce trvala {konec - start} ns")
        return vysledek

    return nahradni_fce
def zkontroluj_opravneni(puvodni_fce):

    @functools.wraps(puvodni_fce)
    def nahradni_fce(kdo, pro_koho, *args, **kwargs):
        if kdo == pro_koho or kdo == "admin":
            return puvodni_fce(kdo, pro_koho,
                               *args, **kwargs)
        else:
            raise ValueError(f"Uživatel {kdo} není "
                             f"oprávněn provést "
                             f"tuto operaci")

    return nahradni_fce

Dekorátory

By Tomáš Bedřich