TI/Programowanie dla Fizyków Medycznych:Dekoratory: Różnice pomiędzy wersjami
(Utworzono nową stronę "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...") |
|||
(Nie pokazano 4 wersji utworzonych przez 2 użytkowników) | |||
Linia 1: | Linia 1: | ||
+ | ==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: | 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: | ||
<source lang="python"> | <source lang="python"> | ||
Linia 74: | Linia 75: | ||
def __call__(self, f): | def __call__(self, f): | ||
def inner(a, b, c): | def inner(a, b, c): | ||
− | print 'parametr dekoratora a =', a | + | print 'parametr dekoratora a =', self.a |
print 'przed wejściem do ', f.__name__ | print 'przed wejściem do ', f.__name__ | ||
f(a, b, c) | f(a, b, c) | ||
Linia 170: | Linia 171: | ||
f() | f() | ||
</source> | </source> | ||
+ | |||
+ | ===Zadanie 0=== | ||
+ | Napisz dekorator, który spowoduje, że przy wywołaniu udekorowanej funkcji wypisze się na ekran informacja po raz który ta funkcja jest wywoływana, np.: "Funkcja <nazwa funkcji> została uruchomiona <n> raz" | ||
+ | |||
+ | ===Zadanie 1=== | ||
+ | Napisz dekorator przechowujący w pamięci wynik wywołania funkcji z jakimś parametrem, tak aby gdy powtórnie wywołamy ją z tym samym parametrem, funkcja nie była wywoływana powtórnie, ale by zwracana była wartość jaką otrzymano wcześniej przy wywołaniu z tym parametrem. | ||
+ | |||
+ | ===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"]] |
Aktualna wersja na dzień 08:08, 10 lis 2017
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 =', self.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 0
Napisz dekorator, który spowoduje, że przy wywołaniu udekorowanej funkcji wypisze się na ekran informacja po raz który ta funkcja jest wywoływana, np.: "Funkcja <nazwa funkcji> została uruchomiona <n> raz"
Zadanie 1
Napisz dekorator przechowujący w pamięci wynik wywołania funkcji z jakimś parametrem, tak aby gdy powtórnie wywołamy ją z tym samym parametrem, funkcja nie była wywoływana powtórnie, ale by zwracana była wartość jaką otrzymano wcześniej przy wywołaniu z tym parametrem.
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