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