decorating decorators: try to get my head around understanding it
Solution 1
Here's a generic (and slightly convoluted) solution for decorating decorators with decorators (Yay!).
# A second-order decorator
def decdec(inner_dec):
def ddmain(outer_dec):
def decwrapper(f):
wrapped = inner_dec(outer_dec(f))
def fwrapper(*args, **kwargs):
return wrapped(*args, **kwargs)
return fwrapper
return decwrapper
return ddmain
def wrap(f):
def wrapper():
return " ".join(f())
return wrapper
# Decorate upper (a decorator) with wrap (another decorator)
@decdec(wrap)
def upper(f):
def uppercase(*args, **kargs):
a,b = f(*args, **kargs)
return a.upper(), b.upper()
return uppercase
@upper
def hello():
return "hello","world"
print(hello())
Solution 2
def upper(f):
@wrap
def uppercase(*args, **kargs):
a,b = f(*args, **kargs)
return a.upper(), b.upper()
return uppercase
A decorator in Python
@foo
def bar(...): ...
is just equivalent to
def bar(...): ...
bar = foo(bar)
You want to get the effect of
@wrap
@upper
def hello(): ....
i.e.
hello = wrap(upper(hello))
so the wrap
should be called on the return value of upper
:
def upper_with_wrap(f):
def uppercase(...): ...
return wrap(uppercase)
which is also equivalent to applying the decorator on that function:
def upper_with_wrap(f):
@wrap
def uppercase(...): ...
# ^ equivalent to 'uppercase = wrap(uppercase)'
return uppercase
Related videos on Youtube
ashwoods
Full time software engineer / part time mathematics student, I like to chase discs and cook in my free time when I am not travelling.
Updated on May 31, 2022Comments
-
ashwoods about 2 years
I'm trying to understand how to decorate decorators, and wanted to try out the following:
Let's say I have two decorators and apply them to the function
hello()
:def wrap(f): def wrapper(): return " ".join(f()) return wrapper def upper(f): def uppercase(*args, **kargs): a,b = f(*args, **kargs) return a.upper(), b.upper() return uppercase @wrap @upper def hello(): return "hello","world" print(hello())
Then I have to start adding other decorators for other functions, but in general the
@wrap
decorator will "wrap all of them"def lower(f): def lowercase(*args, **kargs): a,b = f(*args, **kargs) return a.lower(), b.lower() return lowercase @wrap @lower def byebye(): return "bye", "bye"
How do I write a decorator, which decorates my
@lower
and@upper
decorators? See below:@wrap def lower(): ... @wrap def upper(): ...
To achieve the same result as above by only doing:
@upper def hello(): ... @lower def byebye(): ...
-
e-satis about 13 yearsYou probably want to read this: stackoverflow.com/questions/739654/…. There is an example of decorating decorators at the end, but the answer introduce you slowly to it.
-
-
ashwoods about 13 yearsThis answers the question, almost. You are decorating a decorator, but I wasn't looking for a generic solution, I actually wanted a more simple decorated decorator as somehow my head has to get around the arguments/assignments, no matter how often i go over examples :)
-
ashwoods about 13 yearsthis is a nice solution, but isn't exactly what I was asking, although maybe I should have stated it more clearly in the question. Although using this technique is cleaner (and easier to understand), I was looking into how to explicitly decorate a decorator. You get my upvote though, wish I could give more, specially for the time taken in explaining your answer :)
-
Boaz Yaniv about 13 yearsWell, you can make it non-generic of course. It's even quite simple: write
mywrap = decdec(wrap)
and then use @mywrap as a decorator for decorators. -
Olshansk over 4 yearsExactly what I was looking for!
-
YPCrumble over 2 yearsNote that
@wrap
here has nothing to do with the python builtin@wraps
!