Naloge - dekoratorji

Skip

Napišite dekorator skip, ki bo onemogočil izvajanje dekorirane funkcije, ki sprejme dva argumenta. Namesto pravega rezultata naj dekorirana funkcija vrača None.

def sestej(a,b):
    return a+b

@skip
def deli(a,b):
    return a/b

print(sestej(3,4), deli(6,0))
7 None

Rešitev

def skip(f):
    def wrapper(x, y):
        return None

    return wrapper

Report

Napišite dekorator report, ki bo ob vsakem klicu dekorirane funkcije izpisal njeno ime. Ime funkcije f se skriva v atributu f.__name__. Dekorator naj deluje za funkcije s poljubnim številom argumentov (namig: *args)

@report
def zmnozi(a,b):
    return a*b

report_len = report(len)

print(zmnozi(3,4))
print(report_len([1,2,3]))
zmnozi
12
len
3

Rešitev

def report(f):
    def wrapper(*args):
        print(f.__name__)
        return f(*args)

    return wrapper

Timing

Napišite dekorator timing, ki bo izmeril čas izvajanja dekorirane funkcije. Poleg rezultata funkcije naj vrne tudi čas izvajanja v sekundah.

@timing
def vsota(n):
    return sum(i for i in range(1,n+1))

print(vsota(10**7))
(50000005000000, 0.572)

Rešitev

from time import time

def timing(f):
    def wrapper(*args):
        start = time()
        ret = f(*args)
        end = time()
        return ret, end-start

    return wrapper

Login

Na voljo imamo zelo poenostavljen način nadziranja prijave uporabnika v sistem, ki temelji na globalni spremenljivki logged_in. Prijavo in odjavo uporabnika izvajamo preko funkcij login in logout, ki sta prikladno dekorirani z dekoratorjem report, da lahko sledimo dogajanju. Ukaz global logged_in označuje, da se logged_in znotraj funkcije nanaša na globalno spremenljivko.

logged_in = False

@report
def login():
    global logged_in
    logged_in = True

@report
def logout():
    global logged_in
    logged_in = False

Z uporabo funkcije request(url) lahko uporabnik v sistemu izvaja poizvedbe. Pred izvedbo pa moramo preveriti, ali ima uporabnik pravico izvesti tako poizvedbo, kar v našem primeru pomeni, ali je prijavljen v sistem. Ker obstaja potencialno več takih funkcij, ki bi jih želeli opremiti s funkcionalnostjo preverjanja prijave, napišite dekorator login_required, ki bo pred začetkom izvajanja dekorirane funkcije preveril, ali je uporabnik prijavljen in izvedel prijavo, če ni.

@login_required
def request(url):
    print("Request:", url)

request("/home")
request("/contact")
logout()
request("/menu")
login
Request: /home
Request: /contact
logout
login
Request: /menu

Rešitev

def login_required(f):
    def wrapper(*args):
        if not logged_in:
            login()
        return f(*args)

    return wrapper

Repeat

Napišite dekorator repeat5, ki bo ob vsakem klicu funkcije izvedel le to petkrat in preveril, ali je vseh pet vrnjenih rezultatov enakih. Če ni, naj vrne None, sicer pa enega od petih enakih rezultatov.

from random import randrange

@repeat5
def nakljucno():
    return randrange(10)

sum5 = repeat5(sum)

print(nakljucno(), sum5([2, 1, 4]))
None 7

Rešitev

def repeat5(f):
    def wrapper(*args):
        rezultati = sorted([f(*args) for _ in range(5)])
        return rezultati[0] if rezultati[0] == rezultati[-1] else None

    return wrapper

Posplošite prejšnjo rešitev v parametriziran dekorator repeat(k), ki bo sprejel kot argument tudi število ponovitev k.

Funkcija nic_ena v spodnjem primeru z enako verjetnostjo vrača število 0 ali 1. Kakšna je verjetnost, da bodo rezultati treh zaporednih klicev enaki? Prvi rezultat ni pomemben, drugi in tretji pa bosta enaka prvemu vsak z verjetnostjo 0.5. Pričakujemo lahko torej, da bo 25% klicev tako dekorirane funkcije vrnilo konkreten rezultat in ne None.

@repeat(3)
def nic_ena():
    return randrange(2)

rezultati = []
for i in range(20):
    rezultati.append(nic_ena())
print(rezultati)
[1, None, None, None, None, 1, None, None, None, None, None, None, 0, None, None, None, None, None, 1, None]

Rešitev

def repeat(k):
    def repeat_k(f):
        def wrapper(*args):
            rezultati = sorted([f(*args) for _ in range(k)])
            return rezultati[0] if rezultati[0] == rezultati[-1] else None

        return wrapper

    return repeat_k

Zadnja sprememba: sreda, 29. september 2021, 12.47