J'ai vu de nombreux exemples de décorateurs Python qui sont:
__init__
, __get__
, et __call__
)Mais je n'ai jamais vu un seul exemple qui puisse faire tout ce qui précède, et j'ai du mal à synthétiser à partir de diverses réponses à des questions spécifiques (telles que celle-ci , celle-ci , ou celui-ci (qui a l'une des meilleures réponses que j'ai jamais vues sur SO) ), comment combiner tout cela.
Ce que je veux, c'est un basé sur la classe décorateur qui peut décorer soit une méthode ou une fonction , et qui prend au moins un paramètre supplémentaire . C'est-à-dire pour que les éléments suivants fonctionnent:
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()
Et je m'attendrais à voir:
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: si cela importe, j'utilise Python 2.7.
Vous n'avez pas besoin de jouer avec les descripteurs. Il suffit de créer une fonction wrapper à l'intérieur de la méthode __call__()
et de la renvoyer. Standard Python peuvent toujours agir comme une méthode ou une fonction, selon le contexte:
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
Un peu d'explication sur ce qui se passe lorsque ce décorateur est utilisé comme ceci:
@MyDecorator("some other func!")
def some_other_function():
print "in some other function!"
La première ligne crée une instance de MyDecorator
et passe "some other func!"
Comme argument à __init__()
. Appelons cette instance my_decorator
. Ensuite, l'objet de fonction non décoré - appelons-le bare_func
- est créé et passé à l'instance de décorateur, donc my_decorator(bare_func)
est exécuté. Cela invoquera MyDecorator.__call__()
, qui créera et renverra une fonction wrapper. Enfin, cette fonction wrapper est affectée au nom some_other_function
.
Vous manquez un niveau.
Considérez le code
class Foo(object):
@MyDecorator("foo baby!")
def bar(self):
print "in bar!"
Il est identique à ce code
class Foo(object):
def bar(self):
print "in bar!"
bar = MyDecorator("foo baby!")(bar)
Alors MyDecorator.__init__
est appelé avec "foo baby!"
puis l'objet MyDecorator
est appelé avec la fonction bar
.
Vous voulez peut-être mettre en œuvre quelque chose de plus comme
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
Dans votre liste de types de décorateurs, vous avez raté des décorateurs qui peuvent ou non prendre des arguments. Je pense que cet exemple couvre tous vos types à l'exception des "décorateurs de style de fonction (encapsuler une fonction)"
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