Skip to content

Instantly share code, notes, and snippets.

@dundee
Created June 12, 2017 11:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dundee/c429f02d842ba82283de31aa05b05691 to your computer and use it in GitHub Desktop.
Save dundee/c429f02d842ba82283de31aa05b05691 to your computer and use it in GitHub Desktop.
Decorator for both functions and methods using descriptor
from functools import wraps
class DomainObject:
# nejaky domenovy objekt, ktery vyzaduje hodne parametru
def __init__(self, root, args, context, info):
pass
def process(self):
return 'OK'
# dekorator ktery nam pomuze vytvorit DomainObject
# a bude fungovat jak pro metody tak i funkce
class DomainArgs:
def __init__(self, func):
self.func = func
wraps(func)(self)
def __call__(self, root, args, context, info):
helper = DomainObject(root, args, context, info)
return self.func(helper)
# Vytvorime deskriptor, ktery simuluje chovani metod a funkci v Pythonu:
# kazda funkce je v pythonu deskriptor (ma metodu __get__), takze kdyz funkci
# dame na tridu, tak se pred zavolanim funkce zavola nejdriv jeji metoda __get__, ktera vrati
# boundnutou verzi funkce.
def __get__(self, instance, owner):
# vytvorim boudnutou metodu
# boundnuti dela neco jako functools.partial, tedy preda self a vrati funkci pro dalsi volani
mapped = self.func.__get__(instance, owner)
# vratim sam sebe, pricemz do konstruktoru predam jako func tu boundnutou metodu
return self.__class__(mapped)
class ViewResolver:
# metoda, ve ktere chceme vytvorit DomainObject, ale nechceme vypisovat vsechny parametry, ktere potrebuje
# proto si nechame vytvorit objekt dekoratorem DomainArgs a ten nam preda objekt jako parametr helper
@DomainArgs
def resolve_method(self, helper):
response = helper.process()
return f"Method: {response}"
# to stejne musi fungovat i pro funkci
@DomainArgs
def resolve_method(helper):
response = helper.process()
return f"Method: {response}"
res = ViewResolver().resolve_method('/', [], {}, None)
print(res)
res = resolve_method('/', [], {}, None)
print(res)
# volani metody Trida().metoda('y') se tedy da prepsat jako:
# Trida.metoda(Trida(), 'y')
# a nebo prave pomoci deskriptoru na: Trida.metoda.__get__(Trida())('y')
# coz je to co dela interne Python pro metody
# Trida.metoda.__get__(Trida()) vraci <bound method Trida.metoda...>
# coz je to stejne, co uvidim, kdyz vytvorim instanci Trida a zkusim pristoupit na metodu na te instanci
# Proto u instancnich metod neni videt zvenku uz self, neni to uz ta puvodni "funkce", ale boundnuta metoda, kterou vratil deskriptor.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment