Zapisz jako PDF

Transkrypt

Zapisz jako PDF
Spis treści
1 Dekoratory
1.1 Zadanie 1
1.2 Zadanie 2
1.3 Zadanie 3
1.4 Zadanie 4
Dekoratory
Dekoratory w Pythonie służą do zastępowania zdefiniowanych przez nas funkcji przez funkcje (lub
inne obiekty) zgodnie z definicją dekoratora. Na początku najlepiej zapoznać się z prostym
przykładem dekoratora, który powoduje, że przed i po wywołaniu dekorowanej funkcji wypisywane
są linie informujące o tym jaka funkcja jest wywoływana:
class decorator(object):
def __init__(self, f):
self.f = f
def __call__(self):
print 'przed wejściem do ', self.f.__name__
self.f()
print 'po wyjściu z ', self.f.__name__
@decorator
def g():
print 'wewnątrz g'
g()
Taki sam efekt można osiągnąć wpisując w ciało funkcji g dwie dodatkowe linijki, więc jakie są zalety
wykorzystywania dekoratorów? Po pierwsze jeśli w podobny sposób chcemy zmodyfikować nie jedną
ale wiele funkcji to napisanie dekoratora znacznie ułatwi to zadanie, ponadto dekorowanie funkcji
jest łatwo zauważalne przez czytającego kod (dzięki @), pozwala na logiczne oddzielenie
funkcjonalności na przykład właściwego działania funkcji od kodu realizującego logowanie
użytkownika. Dekorowanie należy rozumieć jako składanie funkcji, kod z przykładu jest równoważny
poniższemu:
class decorator(object):
def __init__(self, f):
self.f = f
def __call__(self):
print 'przed wejściem do ', self.f.__name__
self.f()
print 'po wyjściu z ', self.f.__name__
#@decorator
def g():
print 'wewnątrz g'
g = decorator(g)
g()
Dekoratory można definiować za pomocą klas lub funkcji, można dekorować funkcje mające
argumenty lub nie, w końcu same dekoratory mogą przyjmować argumenty lub nie - omówimy teraz
wszystkie te przypadki. Dekorator bezargumentowy zadany przez klasę musi mieć konstruktor
przyjmujący jeden argument - funkcję, którą dekorujemy i implementować metodę __call__. Jeśli
dekorujemy funkcję, która przyjmuje argumenty i chcemy aby funkcja po udekorowaniu przyjmowała
takie same argumenty to metoda __call__ musi przyjmować takie argumenty:
class decorator(object):
def __init__(self, f):
self.f = f
def __call__(self, a, b, c):
print 'przed wejściem do ', self.f.__name__
self.f(a, b, c)
print 'po wyjściu z ', self.f.__name__
@decorator
def g(a, b, c):
print 'wewnątrz g: a =', a, 'b =', b, 'c =', c
g(1, 2, 3)
W tym przykładzie w czasie dekorowania zostanie wywołany konstruktor klasy decorator, a później
przy wywołaniu funkcji g zostanie wywołana metoda __call__ klasy dekorator. Analogiczny dekorator
zdefiniowany za pomocą funkcji musi być funkcją przyjmującą jeden parametr - funkcję, którą należy
udekorować i zwracającą funkcję, którą funkcja dekorowana będzie zastąpiona:
def decorator(f):
def inner(a, b, c):
print 'przed wejściem do ', f.__name__
f(a, b, c)
print 'po wyjściu z ', f.__name__
return inner
@decorator
def g(a, b, c):
print 'wewnątrz g: a =', a, 'b =', b, 'c =', c
g(1, 2, 3)
Tym razem przy dekorowaniu zostanie wykonana funkcja decorator, a przy wywoływaniu funkcji g
będzie wywoływana funkcja inner Kolejnym zagadnieniem jest tworzenie dekoratorów przyjmujących
argumenty. Dekorator przyjmujący argumenty będący klasą musi mieć konstruktor przyjmujący dane
argumenty i metodę __call__ przyjmującą dokładnie jeden argument - funkcję dekorowaną - i
zwracającą funkcję, którą należy podstawić w miejsce dekorowanej:
class decorator(object):
def __init__(self, a):
self.a = a
def __call__(self, f):
def inner(a, b, c):
print 'parametr dekoratora a =', a
print 'przed wejściem do ', f.__name__
f(a, b, c)
print 'po wyjściu z ', f.__name__
return inner
@decorator(5)
def g(a, b, c):
print 'wewnątrz g: a =', a, 'b =', b, 'c =', c
g(1, 2, 3)
Tym razem w momencie dekorowania zostanie wywołany konstruktor klasy dekorator i metoda
__call__ (będzie ona wywoływana tylko podczas dekorowania), a jej wynik zostanie przypisany na
zmienną przechowującą dekorowaną funkcję. Przy wywołaniu funkcji g będzie wywoływana funkcja
inner. Jeśli chcemy zrealizować analogiczną konstrukcję przy pomocy funkcji to dekorator musi być
funkcją, która przyjmuje parametry dekoratora i zwraca funkcję, która przyjmuje dokładnie jeden
argument - funkcję dekorowaną i zwraca funkcję, która ma być podstawiona w miejsce funkcji
dekorowanej:
def decorator(a):
def wrapper(f):
def inner(a, b, c):
print 'parametr dekoratora a =', a
print 'przed wejściem do ', f.__name__
f(a, b, c)
print 'po wyjściu z ', f.__name__
return inner
return wrapper
@decorator(5)
def g(a, b, c):
print 'wewnątrz g: a =', a, 'b =', b, 'c =', c
g(1, 2, 3)
W tym przypadku podczas dekorowania zostanie wywołana funkcja decorator z argumentem 5 i
funkcja wrapper z argumentem g, wywołując później funkcję g będziemy wywoływali funkcję inner.
W Pythonie mamy dwa wbudowane dekoratory - classmethod i staticmethod - służą one do
definiowania metod w klasach, które nie wymagają instancji klasy do ich wywołania - mogą być
wywoływane przez klasę - metody udekorowane classmetod jako pierwszy parametr otrzymują klasę
na rzecz której zostały wywołane, a staticmethod nie wiedzą nawet na rzecz jakiej klasy zostały
wywołane:
class A(object):
def normalna(self):
print "metoda normalna, wywołana na obiekcie", self
@classmethod
def klasowa(cls):
print "metoda klasowa, wywołana na klasie", cls
@staticmethod
def statyczna():
print "metoda statyczna"
A.klasowa()
A.statyczna()
A().normalna()
Zauważ, że do wywołania metod statyczna() i klasowa() nie potrzebowaliśmy tworzyć obiektu klasy
A, z kolei próba wywołania metody normalna() na klasie powoduje wyjątek:
>>> A.normalna()
Traceback (most recent call last):
File "<pyshell#195>", line 1, in <module>
A.normalna()
TypeError: unbound method normalna() must be called with A instance as first
argument (got nothing instead)
Dekoratory można też wykorzystywać do modyfikowania klas, jeśli na przykład chcemy dodać
zmienną klasową a i metodę b do klasy A możemy stworzyć następujący dekorator:
def decorator(C):
C.a = 5
def b(self):
print self
C.b = b
return C
@decorator
class A(object):
pass
a = A()
a.b()
print a.a
Funkcje i klasy można dekorować wieloma dekoratorami na raz:
def A(f):
def inner():
print 'dekorator A'
f()
return inner
def B(f):
def inner():
print 'dekorator B'
f()
return inner
@A
@B
def f():
print 'funkcja f'
f()
Zadanie 1
Napisz dekorator modelujący pamięć dla funkcji, tak aby gdy powtórnie wywołamy ją z jakimiś
parametrami funkcja nie była wywoływana i zwracana była wartość jaką otrzymano wcześniej dla
tych samych argumentów.
Zadanie 2
Napisz dekorator do funkcji zwracającej wartość logiczną wywołujący funkcję do czasu gdy zwróci
True. Przetestuj na funkcji czytającej z wejścia i sprawdzającej czy użytkownik wpisał 'python'. Niech
dekorator jako argument przyjmuje liczbę całkowitą oznaczającą maksymalną liczbę prób wywołania
funkcji dekorowanej.
Zadanie 3
Napisz dekorator, który będzie wymagał podania hasła przed właściwym wywołaniem funkcji, jeśli
zostanie podane błędne hasło to niech będzie wypisany komunikat o braku dostępu.
Zadanie 4
Napisz dekorator rejestrujący funkcje do listy będącej parametrem dekoratora, a następnie wywołaj
wszystkie funkcje z listy
"Programowanie dla Fizyków Medycznych"

Podobne dokumenty