Dekoratory

  • obiekty, które są callable
  • specjalna składnia @
  • modyfikacje runtime funkcji, metod, klas
  • od wersji Python 2.4
  • implementacja w formie funkcji albo klasy

In [ ]:
def greetings(f):
    print(f)
    def wrapper(*args, **kwargs):
        print("dekorator foo mówi: ", "Hello world!")
        return f(*args, **kwargs)
    
    return wrapper
    
@greetings    
def foo(a, b):
    print(a, b)
    print("Foo function")
    
def bar():
    pass
    
foo(4, 5)
#foo(1, 2)
#print(foo)
#print(bar)

In [ ]:
def greetings(a, b, *args, **kwargs):
    print("parametry funkcji tworzącej dekorator: ", a, b, args, kwargs)
    def wrapper(f):
        def inner_wrapper(*args, **kwargs):
            print("dekorator foo mówi: ", "Hello world!")
            print("parametry przekazane do foo: ", args, kwargs)
            return f(*args, **kwargs)
        return inner_wrapper
    
    return wrapper
    
@greetings(1, 2, 3, liczba10=10, liczba20=20)  
def foo(a, b):
    print(a, b) 
    print("Foo function")
    
def bar():
    pass  
    
foo(4, 5)
#print(foo)
#print(bar)

?


In [ ]:
def greetings(f):
    def wrapper(*args, **kwargs):
        print("greetings")
        f(*args, **kwargs)
    return wrapper

def hello(f):
    def wrapper(*args, **kwargs):
        print("hello world")
        f(*args, **kwargs)
    return wrapper

@greetings
@hello
def foo():
    print("foo")
    
foo()

In [ ]:
def attrs(**kwargs):
    def decorate(f):
        for k in kwargs:
            setattr(f, k, kwargs[k])
        return f
    return decorate

@attrs(version="1.0", author="Andrzej Krawczyk")
def foo():
    print("Hi!")
    
print(foo.version)
print(foo.author)
print(foo())

moduł functools

  • Zawiera funkcje, zwracające funkcje albo działające na funkcjach
  • wiele przydatnych zaimplementowanych rozwiązań
  • wrapper_update i wraps, signal dispatch, lru_cache, partial

wraps

  • dekorator na update_wrapper
  • functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)
  • __module__, __name__, __qualname__, __annotations__ and __doc__ wrapper assignemnts
  • __dict__ wrapper updates

In [ ]:
from functools import wraps

def greetings(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        print("Parametry przekazane do nowej funkcji: ", args, kwargs)
        return f(*args, **kwargs)
    
    return wrapper
    
@greetings    
def foo(a, b):
    """Docstring funkcji foo"""
    print(a, b)
    print("Foo function")
    
foo(4, 5)
#print(foo.__wrapped__)
#print(foo)
#print(foo.__doc__)

In [ ]:
class greetings():
    def __init__(self, f):
        self.f = f
    
    def __call__(self, *args, **kwargs):
        print("pozdrowienia z dekoratora w formie klasy", self.f)
        return self.f(*args, **kwargs)
    
    
@greetings    
def foo(a, b):
    print(a, b)
    print("Foo function")
    
def bar():
    pass
    
foo(4, 5)
print(foo)
print(bar)

In [ ]:
from functools import wraps

class greetings():
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c
    
    def __call__(self, f, *args, **kwargs):
        @wraps(f)
        def wrapper(*args, **kwargs):
            print("pozdrowienia z dekoratora w formie klasy", f)
            print(self.a, self.b, self.c)
            return f(*args, **kwargs)
        return wrapper
    
    
@greetings("ala", "ma", "kota")    
def foo(a, b):
    print("Foo function", a, b)
    
def bar():
    pass
    
foo(4, 5)
print(foo)
print(bar)

singledispatch

  • dekorator pozwalający symulować przeciążenia funkcji
  • przeciążenie tylko na pierwszym argumencie funkcji
  • metoda register - rejestrowanie typów z implementacjami funkcji

In [ ]:
from functools import singledispatch

@singledispatch
def suma(arg):
    print("inicjalizacja sumy")
    
@suma.register(tuple)
def _(arg):
    print("suma dla krotki", arg)
    return arg[0] + arg[1]

@suma.register(list)
def _(arg):
    print("suma dla listy", arg)
    return sum(arg)

@suma.register(int)
@suma.register(str)
def _(arg):
    print("dla int lub str", arg)
    
print("int: ", suma(1))
print("str: ", suma("1"))
print("krotka: ", suma((1, 2)))
print("lista: ", suma([1, 2, 3, 4]))

In [ ]:
print(suma.dispatch(int))
print(suma.registry)

lru_cache (least recently used)

  • implementacja dekoratora do cache'owania ostatnich wynków
  • atrybut maxsize - zalecana liczba, która jest n**2
  • atrybut typed (od v3.3) - gdy ustawiony na true, rozróżnia typy np. foo(3) != foo(3.0)
  • cache_info() - metoda zwracająca namedtuple(hits, misses, maxsize, currsize)
  • cache_clear() - czyszczenie cache'a

In [ ]:
from functools import lru_cache
import requests

@lru_cache(maxsize=32, typed=False)
def get_web(url):
    return requests.get(url=url)

In [ ]:
print(get_web("http://www.trojmiasto.pl"))
print(get_web.cache_info())

partial

  • dekorator do tworzenia funkcji z wypełnionymi parametrami
  • upraszczanie sygnatury funkcji

In [ ]:
from functools import partial
from operator import add

def foo(a, b, d=5, e=10):
    print(a, b, d, e)

add_to_five = partial(add, 5)
print(add_to_five(2))
print(add_to_five(10))

p1 = partial(foo, d=-5)
p2 = partial(p1, 5)
p2(-3, e=-100)