Dekorator oparty na klasach Pythona z parametrami, które mogą ozdobić metodę lub funkcję
Widziałem wiele przykładów dekoratorów Pythona, które są:
- dekoratory stylu funkcji (zawijanie funkcji)
- dekoratorzy stylu klasy (realizujący
__init__
,__get__
, i__call__
) - dekoratorzy, którzy nie przyjmują argumentów
- dekoratorzy, którzy biorą argumenty
- dekoratory, które są "przyjazne metodom" (tj. mogą ozdobić metodę w klasie)
- dekoratory, które są "przyjazne dla funkcji "( mogą ozdobić prostą funkcję
- dekoratorzy, którzy mogą udekoruj zarówno metody, jak i funkcje
Ale nigdy nie widziałem żadnego przykładu, który mógłby zrobić wszystkie powyższe, i mam problem z syntetyzowaniem różnych odpowiedzi na konkretne pytania (takie jak ten, ten , lub ten (który ma jedną z najlepszych odpowiedzi, jakie kiedykolwiek widziałem na SO) ), Jak połączyć wszystkie powyższe.
What I want is a oparte na klasach dekorator, który może ozdobić albo metodą, albo funkcja, oraz to wymaga co najmniej jednego dodatkowego parametru. Ie tak, aby działały:
class MyDecorator(object):
def __init__(self, fn, argument):
self.fn = fn
self.arg = argument
def __get__(self, ....):
# voodoo magic for handling distinction between method and function here
def __call__(self, *args, *kwargs):
print "In my decorator before call, with arg %s" % self.arg
self.fn(*args, **kwargs)
print "In my decorator after call, with arg %s" % self.arg
class Foo(object):
@MyDecorator("foo baby!")
def bar(self):
print "in bar!"
@MyDecorator("some other func!")
def some_other_function():
print "in some other function!"
some_other_function()
Foo().bar()
I spodziewałbym się zobaczyć:
In my decorator before call, with arg some other func!
in some other function!
In my decorator after call, with arg some other func!
In my decorator before call, with arg foo baby!
in bar!
In my decorator after call, with arg foo baby!
Edit: jeśli to ma znaczenie, używam Pythona 2.7.
3 answers
Nie musisz zadzierać z deskryptorami. Wystarczy utworzyć funkcję wrappera wewnątrz metody __call__()
i zwrócić ją. Standardowe funkcje Pythona mogą zawsze działać jako metoda lub funkcja, w zależności od kontekstu:
class MyDecorator(object):
def __init__(self, argument):
self.arg = argument
def __call__(self, fn):
@functools.wraps(fn)
def decorated(*args, **kwargs):
print "In my decorator before call, with arg %s" % self.arg
fn(*args, **kwargs)
print "In my decorator after call, with arg %s" % self.arg
return decorated
Trochę wyjaśnienia o tym, co się dzieje, gdy ten dekorator jest używany w ten sposób:
@MyDecorator("some other func!")
def some_other_function():
print "in some other function!"
Pierwsza linia tworzy instancję MyDecorator
i przekazuje "some other func!"
jako argument do __init__()
. Nazwijmy tę instancję my_decorator
. Następny, niedekorowany obiekt function -- nazwijmy go bare_func
-- jest tworzony i przekazywany do instancji dekoratora, więc my_decorator(bare_func)
jest wykonywany. Spowoduje to wywołanie MyDecorator.__call__()
, które utworzy i zwróci funkcję wrappera. Na koniec funkcja ta jest przypisana do nazwy some_other_function
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-23 16:47:07
Brakuje Ci poziomu.
Rozważmy kod
class Foo(object):
@MyDecorator("foo baby!")
def bar(self):
print "in bar!"
Jest identyczny z tym kodem
class Foo(object):
def bar(self):
print "in bar!"
bar = MyDecorator("foo baby!")(bar)
Więc MyDecorator.__init__
jest wywoływany przez "foo baby!"
, a następnie MyDecorator
obiekt jest wywoływany przez funkcję bar
.
Być może chcesz zaimplementować coś bardziej jak
import functools
def MyDecorator(argument):
class _MyDecorator(object):
def __init__(self, fn):
self.fn = fn
def __get__(self, obj, type=None):
return functools.partial(self, obj)
def __call__(self, *args, **kwargs):
print "In my decorator before call, with arg %s" % argument
self.fn(*args, **kwargs)
print "In my decorator after call, with arg %s" % argument
return _MyDecorator
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-10 05:00:01
Na liście typów dekoratorów przegapiłeś dekoratorów, którzy mogą lub nie mogą przyjmować argumentów. Myślę, że ten przykład obejmuje wszystkie typy z wyjątkiem " dekoratorów stylu funkcji (zawijanie funkcji)"
class MyDecorator(object):
def __init__(self, argument):
if hasattr('argument', '__call__'):
self.fn = argument
self.argument = 'default foo baby'
else:
self.argument = argument
def __get__(self, obj, type=None):
return functools.partial(self, obj)
def __call__(self, *args, **kwargs):
if not hasattr(self, 'fn'):
self.fn = args[0]
return self
print "In my decorator before call, with arg %s" % self.argument
self.fn(*args, **kwargs)
print "In my decorator after call, with arg %s" % self.argument
class Foo(object):
@MyDecorator("foo baby!")
def bar(self):
print "in bar!"
class Bar(object):
@MyDecorator
def bar(self):
print "in bar!"
@MyDecorator
def add(a, b):
print a + b
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-22 09:53:29