Decorators with parameters?
![Multi tool use Multi tool use](http://sgv.ssvwv.com/sg/ssvwvcomimagb.png)
Multi tool use
I have a problem with the transfer of variable 'insurance_mode' by the decorator. I would do it by the following decorator statement:
@execute_complete_reservation(True)
def test_booking_gta_object(self):
self.test_select_gta_object()
but unfortunately, this statement does not work. Perhaps maybe there is better way to solve this problem.
def execute_complete_reservation(test_case,insurance_mode):
def inner_function(self,*args,**kwargs):
self.test_create_qsf_query()
test_case(self,*args,**kwargs)
self.test_select_room_option()
if insurance_mode:
self.test_accept_insurance_crosseling()
else:
self.test_decline_insurance_crosseling()
self.test_configure_pax_details()
self.test_configure_payer_details
return inner_function
python decorator
add a comment |
I have a problem with the transfer of variable 'insurance_mode' by the decorator. I would do it by the following decorator statement:
@execute_complete_reservation(True)
def test_booking_gta_object(self):
self.test_select_gta_object()
but unfortunately, this statement does not work. Perhaps maybe there is better way to solve this problem.
def execute_complete_reservation(test_case,insurance_mode):
def inner_function(self,*args,**kwargs):
self.test_create_qsf_query()
test_case(self,*args,**kwargs)
self.test_select_room_option()
if insurance_mode:
self.test_accept_insurance_crosseling()
else:
self.test_decline_insurance_crosseling()
self.test_configure_pax_details()
self.test_configure_payer_details
return inner_function
python decorator
2
Your example is not syntactically valid.execute_complete_reservation
takes two parameters, but you're passing it one. Decorators are just syntactic sugar for wrapping functions inside other functions. See docs.python.org/reference/compound_stmts.html#function for complete documentation.
– Brian Clapper
May 8 '11 at 17:50
add a comment |
I have a problem with the transfer of variable 'insurance_mode' by the decorator. I would do it by the following decorator statement:
@execute_complete_reservation(True)
def test_booking_gta_object(self):
self.test_select_gta_object()
but unfortunately, this statement does not work. Perhaps maybe there is better way to solve this problem.
def execute_complete_reservation(test_case,insurance_mode):
def inner_function(self,*args,**kwargs):
self.test_create_qsf_query()
test_case(self,*args,**kwargs)
self.test_select_room_option()
if insurance_mode:
self.test_accept_insurance_crosseling()
else:
self.test_decline_insurance_crosseling()
self.test_configure_pax_details()
self.test_configure_payer_details
return inner_function
python decorator
I have a problem with the transfer of variable 'insurance_mode' by the decorator. I would do it by the following decorator statement:
@execute_complete_reservation(True)
def test_booking_gta_object(self):
self.test_select_gta_object()
but unfortunately, this statement does not work. Perhaps maybe there is better way to solve this problem.
def execute_complete_reservation(test_case,insurance_mode):
def inner_function(self,*args,**kwargs):
self.test_create_qsf_query()
test_case(self,*args,**kwargs)
self.test_select_room_option()
if insurance_mode:
self.test_accept_insurance_crosseling()
else:
self.test_decline_insurance_crosseling()
self.test_configure_pax_details()
self.test_configure_payer_details
return inner_function
python decorator
python decorator
edited Jun 6 '17 at 17:45
![](https://i.stack.imgur.com/VjcbL.jpg?s=32&g=1)
![](https://i.stack.imgur.com/VjcbL.jpg?s=32&g=1)
martineau
66.9k989180
66.9k989180
asked May 8 '11 at 17:40
falek.marcinfalek.marcin
2,13872131
2,13872131
2
Your example is not syntactically valid.execute_complete_reservation
takes two parameters, but you're passing it one. Decorators are just syntactic sugar for wrapping functions inside other functions. See docs.python.org/reference/compound_stmts.html#function for complete documentation.
– Brian Clapper
May 8 '11 at 17:50
add a comment |
2
Your example is not syntactically valid.execute_complete_reservation
takes two parameters, but you're passing it one. Decorators are just syntactic sugar for wrapping functions inside other functions. See docs.python.org/reference/compound_stmts.html#function for complete documentation.
– Brian Clapper
May 8 '11 at 17:50
2
2
Your example is not syntactically valid.
execute_complete_reservation
takes two parameters, but you're passing it one. Decorators are just syntactic sugar for wrapping functions inside other functions. See docs.python.org/reference/compound_stmts.html#function for complete documentation.– Brian Clapper
May 8 '11 at 17:50
Your example is not syntactically valid.
execute_complete_reservation
takes two parameters, but you're passing it one. Decorators are just syntactic sugar for wrapping functions inside other functions. See docs.python.org/reference/compound_stmts.html#function for complete documentation.– Brian Clapper
May 8 '11 at 17:50
add a comment |
8 Answers
8
active
oldest
votes
You mean def test_booking_gta_object
, right? Anyway, the syntax for decorators with arguments is a bit different - the decorator with arguments should return a function that will take a function and return another function. So it should really return a normal decorator. A bit confusing, right? What I mean is:
def decorator(argument):
def real_decorator(function):
def wrapper(*args, **kwargs):
funny_stuff()
something_with_argument(argument)
result = function(*args, **kwargs)
more_funny_stuff()
return result
return wrapper
return real_decorator
Here you can read more on the subject - it's also possible to implement this using callable objects and that is also explained there.
2
I just did this with lambdas all over the place. (read: Python is awesome!) :)
– Alois Mahdal
Jun 6 '13 at 15:36
35
I wonder why GVR didn't implement it by passing in the parameters as subsequent decorator arguments after 'function'. 'Yo dawg I heard you like closures...' etcetera.
– Michel Müller
Apr 8 '14 at 16:22
14
you forgot VERY USEFUL functools.wraps for decorating wrapper :)
– socketpair
Aug 13 '15 at 21:19
10
You forgot about return when calling function, i.e.return function(*args, **kwargs)
– formiaczek
Dec 1 '15 at 17:09
20
Maybe obvious, but just in case: you need to use this decorator as@decorator()
and not just@decorator
, even if you have only optional arguments.
– Patrick Mevzek
Dec 4 '17 at 20:25
|
show 6 more comments
One way of thinking about decorators with arguments is
@decorator
def foo(*args, **kwargs):
pass
translates to
foo = decorator(foo)
So if the decorator had arguments,
@decorator_with_args(arg)
def foo(*args, **kwargs):
pass
translates to
foo = decorator_with_args(arg)(foo)
decorator_with_args
is a function which accepts a custom argument and which returns the actual decorator (that will be applied to the decorated function).
I use a simple trick with partials to make my decorators easy
from functools import partial
def _pseudo_decor(fun, argument):
def ret_fun(*args, **kwargs):
#do stuff here, for eg.
print ("decorator arg is %s" % str(argument))
return fun(*args, **kwargs)
return ret_fun
real_decorator = partial(_pseudo_decor, argument=arg)
@real_decorator
def foo(*args, **kwargs):
pass
Update:
Above, foo
becomes real_decorator(foo)
One effect of decorating a function is that the name foo
is overridden upon decorator declaration. foo
is "overridden" by whatever is returned by real_decorator
. In this case, a new function object.
All of foo
's metadata is overridden, notably docstring and function name.
>>> print(foo)
<function _pseudo_decor.<locals>.ret_fun at 0x10666a2f0>
functools.wraps gives us a convenient method to "lift" the docstring and name to the returned function.
from functools import partial, wraps
def _pseudo_decor(fun, argument):
# magic sauce to lift the name and doc of the function
@wraps(fun)
def ret_fun(*args, **kwargs):
#do stuff here, for eg.
print ("decorator arg is %s" % str(argument))
return fun(*args, **kwargs)
return ret_fun
real_decorator = partial(_pseudo_decor, argument=arg)
@real_decorator
def bar(*args, **kwargs):
pass
>>> print(bar)
<function __main__.bar(*args, **kwargs)>
1
Your answer perfectly explained the inherent orthogonality of the decorator, thank you
– zsf222
Dec 9 '17 at 15:58
1
life saving answer!.
– saiteja pakalapati
Jul 12 '18 at 11:51
Could you add@functools.wraps
?
– Mr_and_Mrs_D
Aug 26 '18 at 20:48
1
@Mr_and_Mrs_D , I've updated the post with an example withfunctool.wraps
. Adding it in the example may confuse readers further.
– srj
Aug 28 '18 at 2:31
2
What isarg
here!?
– displayname
Sep 25 '18 at 10:22
|
show 5 more comments
I'd like to show an idea which is IMHO quite elegant. The solution proposed by t.dubrownik shows a pattern which is always the same: you need the three-layered wrapper regardless of what the decorator does.
So I thought this is a job for a meta-decorator, that is, a decorator for decorators. As a decorator is a function, it actually works as a regular decorator with arguments:
def parametrized(dec):
def layer(*args, **kwargs):
def repl(f):
return dec(f, *args, **kwargs)
return repl
return layer
This can be applied to a regular decorator in order to add parameters. So for instance, say we have the decorator which doubles the result of a function:
def double(f):
def aux(*xs, **kws):
return 2 * f(*xs, **kws)
return aux
@double
def function(a):
return 10 + a
print function(3) # Prints 26, namely 2 * (10 + 3)
With @parametrized
we can build a generic @multiply
decorator having a parameter
@parametrized
def multiply(f, n):
def aux(*xs, **kws):
return n * f(*xs, **kws)
return aux
@multiply(2)
def function(a):
return 10 + a
print function(3) # Prints 26
@multiply(3)
def function_again(a):
return 10 + a
print function(3) # Keeps printing 26
print function_again(3) # Prints 39, namely 3 * (10 + 3)
Conventionally the first parameter of a parametrized decorator is the function, while the remaining arguments will correspond to the parameter of the parametrized decorator.
An interesting usage example could be a type-safe assertive decorator:
import itertools as it
@parametrized
def types(f, *types):
def rep(*args):
for a, t, n in zip(args, types, it.count()):
if type(a) is not t:
raise TypeError('Value %d has not type %s. %s instead' %
(n, t, type(a))
)
return f(*args)
return rep
@types(str, int) # arg1 is str, arg2 is int
def string_multiply(text, times):
return text * times
print(string_multiply('hello', 3)) # Prints hellohellohello
print(string_multiply(3, 3)) # Fails miserably with TypeError
A final note: here I'm not using functools.wraps
for the wrapper functions, but I would recommend using it all the times.
Didn't use this exactly, but helped me get my head around the concept :) Thanks!
– mouckatron
Oct 10 '17 at 22:04
I tried this and had some issues.
– Jeff
Oct 14 '17 at 14:53
@Jeff could you share with us the kind of issues you had?
– Dacav
Oct 14 '17 at 16:49
I had it linked on my question, and I did figure it out... I needed to call@wraps
in mine for my particular case.
– Jeff
Oct 14 '17 at 19:33
3
Oh boy, I lost a whole day on this. Thankfully, I came around this answer (which incidentally could be the best answer ever created on the whole internet). They too use your@parametrized
trick. The problem I had was I forgot the@
syntax equals actual calls (somehow I knew that and didn't know that at the same time as you can gather from my question). So if you want to translate@
syntax into mundane calls to check how it works, you better comment it out temporarily first or you'd end up calling it twice and getting mumbojumbo results
– z33k
Mar 13 '18 at 14:50
|
show 5 more comments
Here is a slightly modified version of t.dubrownik's answer. Why?
- As a general template, you should return the return value from the original function.
- This changes the name of the function, which could affect other decorators / code.
So use @functools.wraps()
:
from functools import wraps
def decorator(argument):
def real_decorator(function):
@wraps(function)
def wrapper(*args, **kwargs):
funny_stuff()
something_with_argument(argument)
retval = function(*args, **kwargs)
more_funny_stuff()
return retval
return wrapper
return real_decorator
add a comment |
I presume your problem is passing arguments to your decorator. This is a little tricky and not straightforward.
Here's an example of how to do this:
class MyDec(object):
def __init__(self,flag):
self.flag = flag
def __call__(self, original_func):
decorator_self = self
def wrappee( *args, **kwargs):
print 'in decorator before wrapee with flag ',decorator_self.flag
original_func(*args,**kwargs)
print 'in decorator after wrapee with flag ',decorator_self.flag
return wrappee
@MyDec('foo de fa fa')
def bar(a,b,c):
print 'in bar',a,b,c
bar('x','y','z')
Prints:
in decorator before wrapee with flag foo de fa fa
in bar x y z
in decorator after wrapee with flag foo de fa fa
See Bruce Eckel's article for more details.
19
Beware of decorator classes. They don't work on methods unless you manually reinvent the logic of instancemethod descriptors.
– user395760
May 8 '11 at 18:01
9
delnan, care to elaborate? I've only had to use this pattern once, so I haven't hit any of the pitfalls yet.
– Ross Rogers
May 8 '11 at 18:04
2
@RossRogers My guess is that @delnan is referring to things like__name__
which an instance of the decorator class won't have?
– jamesc
Jan 13 '14 at 17:18
9
@jamesc That too, though that's relatively easy to solve. The specific case I was referring to wasclass Foo: @MyDec(...) def method(self, ...): blah
which does not work becauseFoo().method
won't be a bound method and won't passself
automatically. This too can be fixed, by makingMyDec
a descriptor and creating bound methods in__get__
, but it's more involved and much less obvious. In the end, decorator classes are not as convenient as they seem.
– user395760
Jan 13 '14 at 21:49
2
@delnan I'd like to see this caveat featured more prominently. I'm hitting it and am interested in seeing a solution that DOES work (more involved an less obvious though it may be).
– HaPsantran
Mar 13 '16 at 6:42
|
show 1 more comment
In my instance, I decided to solve this via a one-line lambda to create a new decorator function:
def finished_message(function, message="Finished!"):
def wrapper(*args, **kwargs):
output = function(*args,**kwargs)
print(message)
return output
return wrapper
@finished_message
def func():
pass
my_finished_message = lambda f: finished_message(f, "All Done!")
@my_finished_message
def my_func():
pass
if __name__ == '__main__':
func()
my_func()
When executed, this prints:
Finished!
All Done!
Perhaps not as extensible as other solutions, but worked for me.
This works. Although yes, this makes it hard to set the value to the decorator.
– Arindam Roychowdhury
Jan 11 at 11:28
add a comment |
define this "decoratorize function" to generate customized decorator function:
def decoratorize(FUN, **kw):
def foo(*args, **kws):
return FUN(*args, **kws, **kw)
return foo
use it this way:
@decoratorize(FUN, arg1 = , arg2 = , ...)
def bar(...):
...
add a comment |
def decorator(argument):
def real_decorator(function):
def wrapper(*args):
for arg in args:
assert type(arg)==int,f'{arg} is not an interger'
result = function(*args)
result = result*argument
return result
return wrapper
return real_decorator
Usage of the decorator
@decorator(2)
def adder(*args):
sum=0
for i in args:
sum+=i
return sum
Then the
adder(2,3)
produces
10
but
adder('hi',3)
produces
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
<ipython-input-143-242a8feb1cc4> in <module>
----> 1 adder('hi',3)
<ipython-input-140-d3420c248ebd> in wrapper(*args)
3 def wrapper(*args):
4 for arg in args:
----> 5 assert type(arg)==int,f'{arg} is not an interger'
6 result = function(*args)
7 result = result*argument
AssertionError: hi is not an interger
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f5929107%2fdecorators-with-parameters%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
8 Answers
8
active
oldest
votes
8 Answers
8
active
oldest
votes
active
oldest
votes
active
oldest
votes
You mean def test_booking_gta_object
, right? Anyway, the syntax for decorators with arguments is a bit different - the decorator with arguments should return a function that will take a function and return another function. So it should really return a normal decorator. A bit confusing, right? What I mean is:
def decorator(argument):
def real_decorator(function):
def wrapper(*args, **kwargs):
funny_stuff()
something_with_argument(argument)
result = function(*args, **kwargs)
more_funny_stuff()
return result
return wrapper
return real_decorator
Here you can read more on the subject - it's also possible to implement this using callable objects and that is also explained there.
2
I just did this with lambdas all over the place. (read: Python is awesome!) :)
– Alois Mahdal
Jun 6 '13 at 15:36
35
I wonder why GVR didn't implement it by passing in the parameters as subsequent decorator arguments after 'function'. 'Yo dawg I heard you like closures...' etcetera.
– Michel Müller
Apr 8 '14 at 16:22
14
you forgot VERY USEFUL functools.wraps for decorating wrapper :)
– socketpair
Aug 13 '15 at 21:19
10
You forgot about return when calling function, i.e.return function(*args, **kwargs)
– formiaczek
Dec 1 '15 at 17:09
20
Maybe obvious, but just in case: you need to use this decorator as@decorator()
and not just@decorator
, even if you have only optional arguments.
– Patrick Mevzek
Dec 4 '17 at 20:25
|
show 6 more comments
You mean def test_booking_gta_object
, right? Anyway, the syntax for decorators with arguments is a bit different - the decorator with arguments should return a function that will take a function and return another function. So it should really return a normal decorator. A bit confusing, right? What I mean is:
def decorator(argument):
def real_decorator(function):
def wrapper(*args, **kwargs):
funny_stuff()
something_with_argument(argument)
result = function(*args, **kwargs)
more_funny_stuff()
return result
return wrapper
return real_decorator
Here you can read more on the subject - it's also possible to implement this using callable objects and that is also explained there.
2
I just did this with lambdas all over the place. (read: Python is awesome!) :)
– Alois Mahdal
Jun 6 '13 at 15:36
35
I wonder why GVR didn't implement it by passing in the parameters as subsequent decorator arguments after 'function'. 'Yo dawg I heard you like closures...' etcetera.
– Michel Müller
Apr 8 '14 at 16:22
14
you forgot VERY USEFUL functools.wraps for decorating wrapper :)
– socketpair
Aug 13 '15 at 21:19
10
You forgot about return when calling function, i.e.return function(*args, **kwargs)
– formiaczek
Dec 1 '15 at 17:09
20
Maybe obvious, but just in case: you need to use this decorator as@decorator()
and not just@decorator
, even if you have only optional arguments.
– Patrick Mevzek
Dec 4 '17 at 20:25
|
show 6 more comments
You mean def test_booking_gta_object
, right? Anyway, the syntax for decorators with arguments is a bit different - the decorator with arguments should return a function that will take a function and return another function. So it should really return a normal decorator. A bit confusing, right? What I mean is:
def decorator(argument):
def real_decorator(function):
def wrapper(*args, **kwargs):
funny_stuff()
something_with_argument(argument)
result = function(*args, **kwargs)
more_funny_stuff()
return result
return wrapper
return real_decorator
Here you can read more on the subject - it's also possible to implement this using callable objects and that is also explained there.
You mean def test_booking_gta_object
, right? Anyway, the syntax for decorators with arguments is a bit different - the decorator with arguments should return a function that will take a function and return another function. So it should really return a normal decorator. A bit confusing, right? What I mean is:
def decorator(argument):
def real_decorator(function):
def wrapper(*args, **kwargs):
funny_stuff()
something_with_argument(argument)
result = function(*args, **kwargs)
more_funny_stuff()
return result
return wrapper
return real_decorator
Here you can read more on the subject - it's also possible to implement this using callable objects and that is also explained there.
edited Apr 21 '18 at 18:38
Zen
1,02331533
1,02331533
answered May 8 '11 at 17:54
t.dubrownikt.dubrownik
5,9761115
5,9761115
2
I just did this with lambdas all over the place. (read: Python is awesome!) :)
– Alois Mahdal
Jun 6 '13 at 15:36
35
I wonder why GVR didn't implement it by passing in the parameters as subsequent decorator arguments after 'function'. 'Yo dawg I heard you like closures...' etcetera.
– Michel Müller
Apr 8 '14 at 16:22
14
you forgot VERY USEFUL functools.wraps for decorating wrapper :)
– socketpair
Aug 13 '15 at 21:19
10
You forgot about return when calling function, i.e.return function(*args, **kwargs)
– formiaczek
Dec 1 '15 at 17:09
20
Maybe obvious, but just in case: you need to use this decorator as@decorator()
and not just@decorator
, even if you have only optional arguments.
– Patrick Mevzek
Dec 4 '17 at 20:25
|
show 6 more comments
2
I just did this with lambdas all over the place. (read: Python is awesome!) :)
– Alois Mahdal
Jun 6 '13 at 15:36
35
I wonder why GVR didn't implement it by passing in the parameters as subsequent decorator arguments after 'function'. 'Yo dawg I heard you like closures...' etcetera.
– Michel Müller
Apr 8 '14 at 16:22
14
you forgot VERY USEFUL functools.wraps for decorating wrapper :)
– socketpair
Aug 13 '15 at 21:19
10
You forgot about return when calling function, i.e.return function(*args, **kwargs)
– formiaczek
Dec 1 '15 at 17:09
20
Maybe obvious, but just in case: you need to use this decorator as@decorator()
and not just@decorator
, even if you have only optional arguments.
– Patrick Mevzek
Dec 4 '17 at 20:25
2
2
I just did this with lambdas all over the place. (read: Python is awesome!) :)
– Alois Mahdal
Jun 6 '13 at 15:36
I just did this with lambdas all over the place. (read: Python is awesome!) :)
– Alois Mahdal
Jun 6 '13 at 15:36
35
35
I wonder why GVR didn't implement it by passing in the parameters as subsequent decorator arguments after 'function'. 'Yo dawg I heard you like closures...' etcetera.
– Michel Müller
Apr 8 '14 at 16:22
I wonder why GVR didn't implement it by passing in the parameters as subsequent decorator arguments after 'function'. 'Yo dawg I heard you like closures...' etcetera.
– Michel Müller
Apr 8 '14 at 16:22
14
14
you forgot VERY USEFUL functools.wraps for decorating wrapper :)
– socketpair
Aug 13 '15 at 21:19
you forgot VERY USEFUL functools.wraps for decorating wrapper :)
– socketpair
Aug 13 '15 at 21:19
10
10
You forgot about return when calling function, i.e.
return function(*args, **kwargs)
– formiaczek
Dec 1 '15 at 17:09
You forgot about return when calling function, i.e.
return function(*args, **kwargs)
– formiaczek
Dec 1 '15 at 17:09
20
20
Maybe obvious, but just in case: you need to use this decorator as
@decorator()
and not just @decorator
, even if you have only optional arguments.– Patrick Mevzek
Dec 4 '17 at 20:25
Maybe obvious, but just in case: you need to use this decorator as
@decorator()
and not just @decorator
, even if you have only optional arguments.– Patrick Mevzek
Dec 4 '17 at 20:25
|
show 6 more comments
One way of thinking about decorators with arguments is
@decorator
def foo(*args, **kwargs):
pass
translates to
foo = decorator(foo)
So if the decorator had arguments,
@decorator_with_args(arg)
def foo(*args, **kwargs):
pass
translates to
foo = decorator_with_args(arg)(foo)
decorator_with_args
is a function which accepts a custom argument and which returns the actual decorator (that will be applied to the decorated function).
I use a simple trick with partials to make my decorators easy
from functools import partial
def _pseudo_decor(fun, argument):
def ret_fun(*args, **kwargs):
#do stuff here, for eg.
print ("decorator arg is %s" % str(argument))
return fun(*args, **kwargs)
return ret_fun
real_decorator = partial(_pseudo_decor, argument=arg)
@real_decorator
def foo(*args, **kwargs):
pass
Update:
Above, foo
becomes real_decorator(foo)
One effect of decorating a function is that the name foo
is overridden upon decorator declaration. foo
is "overridden" by whatever is returned by real_decorator
. In this case, a new function object.
All of foo
's metadata is overridden, notably docstring and function name.
>>> print(foo)
<function _pseudo_decor.<locals>.ret_fun at 0x10666a2f0>
functools.wraps gives us a convenient method to "lift" the docstring and name to the returned function.
from functools import partial, wraps
def _pseudo_decor(fun, argument):
# magic sauce to lift the name and doc of the function
@wraps(fun)
def ret_fun(*args, **kwargs):
#do stuff here, for eg.
print ("decorator arg is %s" % str(argument))
return fun(*args, **kwargs)
return ret_fun
real_decorator = partial(_pseudo_decor, argument=arg)
@real_decorator
def bar(*args, **kwargs):
pass
>>> print(bar)
<function __main__.bar(*args, **kwargs)>
1
Your answer perfectly explained the inherent orthogonality of the decorator, thank you
– zsf222
Dec 9 '17 at 15:58
1
life saving answer!.
– saiteja pakalapati
Jul 12 '18 at 11:51
Could you add@functools.wraps
?
– Mr_and_Mrs_D
Aug 26 '18 at 20:48
1
@Mr_and_Mrs_D , I've updated the post with an example withfunctool.wraps
. Adding it in the example may confuse readers further.
– srj
Aug 28 '18 at 2:31
2
What isarg
here!?
– displayname
Sep 25 '18 at 10:22
|
show 5 more comments
One way of thinking about decorators with arguments is
@decorator
def foo(*args, **kwargs):
pass
translates to
foo = decorator(foo)
So if the decorator had arguments,
@decorator_with_args(arg)
def foo(*args, **kwargs):
pass
translates to
foo = decorator_with_args(arg)(foo)
decorator_with_args
is a function which accepts a custom argument and which returns the actual decorator (that will be applied to the decorated function).
I use a simple trick with partials to make my decorators easy
from functools import partial
def _pseudo_decor(fun, argument):
def ret_fun(*args, **kwargs):
#do stuff here, for eg.
print ("decorator arg is %s" % str(argument))
return fun(*args, **kwargs)
return ret_fun
real_decorator = partial(_pseudo_decor, argument=arg)
@real_decorator
def foo(*args, **kwargs):
pass
Update:
Above, foo
becomes real_decorator(foo)
One effect of decorating a function is that the name foo
is overridden upon decorator declaration. foo
is "overridden" by whatever is returned by real_decorator
. In this case, a new function object.
All of foo
's metadata is overridden, notably docstring and function name.
>>> print(foo)
<function _pseudo_decor.<locals>.ret_fun at 0x10666a2f0>
functools.wraps gives us a convenient method to "lift" the docstring and name to the returned function.
from functools import partial, wraps
def _pseudo_decor(fun, argument):
# magic sauce to lift the name and doc of the function
@wraps(fun)
def ret_fun(*args, **kwargs):
#do stuff here, for eg.
print ("decorator arg is %s" % str(argument))
return fun(*args, **kwargs)
return ret_fun
real_decorator = partial(_pseudo_decor, argument=arg)
@real_decorator
def bar(*args, **kwargs):
pass
>>> print(bar)
<function __main__.bar(*args, **kwargs)>
1
Your answer perfectly explained the inherent orthogonality of the decorator, thank you
– zsf222
Dec 9 '17 at 15:58
1
life saving answer!.
– saiteja pakalapati
Jul 12 '18 at 11:51
Could you add@functools.wraps
?
– Mr_and_Mrs_D
Aug 26 '18 at 20:48
1
@Mr_and_Mrs_D , I've updated the post with an example withfunctool.wraps
. Adding it in the example may confuse readers further.
– srj
Aug 28 '18 at 2:31
2
What isarg
here!?
– displayname
Sep 25 '18 at 10:22
|
show 5 more comments
One way of thinking about decorators with arguments is
@decorator
def foo(*args, **kwargs):
pass
translates to
foo = decorator(foo)
So if the decorator had arguments,
@decorator_with_args(arg)
def foo(*args, **kwargs):
pass
translates to
foo = decorator_with_args(arg)(foo)
decorator_with_args
is a function which accepts a custom argument and which returns the actual decorator (that will be applied to the decorated function).
I use a simple trick with partials to make my decorators easy
from functools import partial
def _pseudo_decor(fun, argument):
def ret_fun(*args, **kwargs):
#do stuff here, for eg.
print ("decorator arg is %s" % str(argument))
return fun(*args, **kwargs)
return ret_fun
real_decorator = partial(_pseudo_decor, argument=arg)
@real_decorator
def foo(*args, **kwargs):
pass
Update:
Above, foo
becomes real_decorator(foo)
One effect of decorating a function is that the name foo
is overridden upon decorator declaration. foo
is "overridden" by whatever is returned by real_decorator
. In this case, a new function object.
All of foo
's metadata is overridden, notably docstring and function name.
>>> print(foo)
<function _pseudo_decor.<locals>.ret_fun at 0x10666a2f0>
functools.wraps gives us a convenient method to "lift" the docstring and name to the returned function.
from functools import partial, wraps
def _pseudo_decor(fun, argument):
# magic sauce to lift the name and doc of the function
@wraps(fun)
def ret_fun(*args, **kwargs):
#do stuff here, for eg.
print ("decorator arg is %s" % str(argument))
return fun(*args, **kwargs)
return ret_fun
real_decorator = partial(_pseudo_decor, argument=arg)
@real_decorator
def bar(*args, **kwargs):
pass
>>> print(bar)
<function __main__.bar(*args, **kwargs)>
One way of thinking about decorators with arguments is
@decorator
def foo(*args, **kwargs):
pass
translates to
foo = decorator(foo)
So if the decorator had arguments,
@decorator_with_args(arg)
def foo(*args, **kwargs):
pass
translates to
foo = decorator_with_args(arg)(foo)
decorator_with_args
is a function which accepts a custom argument and which returns the actual decorator (that will be applied to the decorated function).
I use a simple trick with partials to make my decorators easy
from functools import partial
def _pseudo_decor(fun, argument):
def ret_fun(*args, **kwargs):
#do stuff here, for eg.
print ("decorator arg is %s" % str(argument))
return fun(*args, **kwargs)
return ret_fun
real_decorator = partial(_pseudo_decor, argument=arg)
@real_decorator
def foo(*args, **kwargs):
pass
Update:
Above, foo
becomes real_decorator(foo)
One effect of decorating a function is that the name foo
is overridden upon decorator declaration. foo
is "overridden" by whatever is returned by real_decorator
. In this case, a new function object.
All of foo
's metadata is overridden, notably docstring and function name.
>>> print(foo)
<function _pseudo_decor.<locals>.ret_fun at 0x10666a2f0>
functools.wraps gives us a convenient method to "lift" the docstring and name to the returned function.
from functools import partial, wraps
def _pseudo_decor(fun, argument):
# magic sauce to lift the name and doc of the function
@wraps(fun)
def ret_fun(*args, **kwargs):
#do stuff here, for eg.
print ("decorator arg is %s" % str(argument))
return fun(*args, **kwargs)
return ret_fun
real_decorator = partial(_pseudo_decor, argument=arg)
@real_decorator
def bar(*args, **kwargs):
pass
>>> print(bar)
<function __main__.bar(*args, **kwargs)>
edited Aug 28 '18 at 2:31
answered Sep 13 '14 at 19:52
srjsrj
5,15421424
5,15421424
1
Your answer perfectly explained the inherent orthogonality of the decorator, thank you
– zsf222
Dec 9 '17 at 15:58
1
life saving answer!.
– saiteja pakalapati
Jul 12 '18 at 11:51
Could you add@functools.wraps
?
– Mr_and_Mrs_D
Aug 26 '18 at 20:48
1
@Mr_and_Mrs_D , I've updated the post with an example withfunctool.wraps
. Adding it in the example may confuse readers further.
– srj
Aug 28 '18 at 2:31
2
What isarg
here!?
– displayname
Sep 25 '18 at 10:22
|
show 5 more comments
1
Your answer perfectly explained the inherent orthogonality of the decorator, thank you
– zsf222
Dec 9 '17 at 15:58
1
life saving answer!.
– saiteja pakalapati
Jul 12 '18 at 11:51
Could you add@functools.wraps
?
– Mr_and_Mrs_D
Aug 26 '18 at 20:48
1
@Mr_and_Mrs_D , I've updated the post with an example withfunctool.wraps
. Adding it in the example may confuse readers further.
– srj
Aug 28 '18 at 2:31
2
What isarg
here!?
– displayname
Sep 25 '18 at 10:22
1
1
Your answer perfectly explained the inherent orthogonality of the decorator, thank you
– zsf222
Dec 9 '17 at 15:58
Your answer perfectly explained the inherent orthogonality of the decorator, thank you
– zsf222
Dec 9 '17 at 15:58
1
1
life saving answer!.
– saiteja pakalapati
Jul 12 '18 at 11:51
life saving answer!.
– saiteja pakalapati
Jul 12 '18 at 11:51
Could you add
@functools.wraps
?– Mr_and_Mrs_D
Aug 26 '18 at 20:48
Could you add
@functools.wraps
?– Mr_and_Mrs_D
Aug 26 '18 at 20:48
1
1
@Mr_and_Mrs_D , I've updated the post with an example with
functool.wraps
. Adding it in the example may confuse readers further.– srj
Aug 28 '18 at 2:31
@Mr_and_Mrs_D , I've updated the post with an example with
functool.wraps
. Adding it in the example may confuse readers further.– srj
Aug 28 '18 at 2:31
2
2
What is
arg
here!?– displayname
Sep 25 '18 at 10:22
What is
arg
here!?– displayname
Sep 25 '18 at 10:22
|
show 5 more comments
I'd like to show an idea which is IMHO quite elegant. The solution proposed by t.dubrownik shows a pattern which is always the same: you need the three-layered wrapper regardless of what the decorator does.
So I thought this is a job for a meta-decorator, that is, a decorator for decorators. As a decorator is a function, it actually works as a regular decorator with arguments:
def parametrized(dec):
def layer(*args, **kwargs):
def repl(f):
return dec(f, *args, **kwargs)
return repl
return layer
This can be applied to a regular decorator in order to add parameters. So for instance, say we have the decorator which doubles the result of a function:
def double(f):
def aux(*xs, **kws):
return 2 * f(*xs, **kws)
return aux
@double
def function(a):
return 10 + a
print function(3) # Prints 26, namely 2 * (10 + 3)
With @parametrized
we can build a generic @multiply
decorator having a parameter
@parametrized
def multiply(f, n):
def aux(*xs, **kws):
return n * f(*xs, **kws)
return aux
@multiply(2)
def function(a):
return 10 + a
print function(3) # Prints 26
@multiply(3)
def function_again(a):
return 10 + a
print function(3) # Keeps printing 26
print function_again(3) # Prints 39, namely 3 * (10 + 3)
Conventionally the first parameter of a parametrized decorator is the function, while the remaining arguments will correspond to the parameter of the parametrized decorator.
An interesting usage example could be a type-safe assertive decorator:
import itertools as it
@parametrized
def types(f, *types):
def rep(*args):
for a, t, n in zip(args, types, it.count()):
if type(a) is not t:
raise TypeError('Value %d has not type %s. %s instead' %
(n, t, type(a))
)
return f(*args)
return rep
@types(str, int) # arg1 is str, arg2 is int
def string_multiply(text, times):
return text * times
print(string_multiply('hello', 3)) # Prints hellohellohello
print(string_multiply(3, 3)) # Fails miserably with TypeError
A final note: here I'm not using functools.wraps
for the wrapper functions, but I would recommend using it all the times.
Didn't use this exactly, but helped me get my head around the concept :) Thanks!
– mouckatron
Oct 10 '17 at 22:04
I tried this and had some issues.
– Jeff
Oct 14 '17 at 14:53
@Jeff could you share with us the kind of issues you had?
– Dacav
Oct 14 '17 at 16:49
I had it linked on my question, and I did figure it out... I needed to call@wraps
in mine for my particular case.
– Jeff
Oct 14 '17 at 19:33
3
Oh boy, I lost a whole day on this. Thankfully, I came around this answer (which incidentally could be the best answer ever created on the whole internet). They too use your@parametrized
trick. The problem I had was I forgot the@
syntax equals actual calls (somehow I knew that and didn't know that at the same time as you can gather from my question). So if you want to translate@
syntax into mundane calls to check how it works, you better comment it out temporarily first or you'd end up calling it twice and getting mumbojumbo results
– z33k
Mar 13 '18 at 14:50
|
show 5 more comments
I'd like to show an idea which is IMHO quite elegant. The solution proposed by t.dubrownik shows a pattern which is always the same: you need the three-layered wrapper regardless of what the decorator does.
So I thought this is a job for a meta-decorator, that is, a decorator for decorators. As a decorator is a function, it actually works as a regular decorator with arguments:
def parametrized(dec):
def layer(*args, **kwargs):
def repl(f):
return dec(f, *args, **kwargs)
return repl
return layer
This can be applied to a regular decorator in order to add parameters. So for instance, say we have the decorator which doubles the result of a function:
def double(f):
def aux(*xs, **kws):
return 2 * f(*xs, **kws)
return aux
@double
def function(a):
return 10 + a
print function(3) # Prints 26, namely 2 * (10 + 3)
With @parametrized
we can build a generic @multiply
decorator having a parameter
@parametrized
def multiply(f, n):
def aux(*xs, **kws):
return n * f(*xs, **kws)
return aux
@multiply(2)
def function(a):
return 10 + a
print function(3) # Prints 26
@multiply(3)
def function_again(a):
return 10 + a
print function(3) # Keeps printing 26
print function_again(3) # Prints 39, namely 3 * (10 + 3)
Conventionally the first parameter of a parametrized decorator is the function, while the remaining arguments will correspond to the parameter of the parametrized decorator.
An interesting usage example could be a type-safe assertive decorator:
import itertools as it
@parametrized
def types(f, *types):
def rep(*args):
for a, t, n in zip(args, types, it.count()):
if type(a) is not t:
raise TypeError('Value %d has not type %s. %s instead' %
(n, t, type(a))
)
return f(*args)
return rep
@types(str, int) # arg1 is str, arg2 is int
def string_multiply(text, times):
return text * times
print(string_multiply('hello', 3)) # Prints hellohellohello
print(string_multiply(3, 3)) # Fails miserably with TypeError
A final note: here I'm not using functools.wraps
for the wrapper functions, but I would recommend using it all the times.
Didn't use this exactly, but helped me get my head around the concept :) Thanks!
– mouckatron
Oct 10 '17 at 22:04
I tried this and had some issues.
– Jeff
Oct 14 '17 at 14:53
@Jeff could you share with us the kind of issues you had?
– Dacav
Oct 14 '17 at 16:49
I had it linked on my question, and I did figure it out... I needed to call@wraps
in mine for my particular case.
– Jeff
Oct 14 '17 at 19:33
3
Oh boy, I lost a whole day on this. Thankfully, I came around this answer (which incidentally could be the best answer ever created on the whole internet). They too use your@parametrized
trick. The problem I had was I forgot the@
syntax equals actual calls (somehow I knew that and didn't know that at the same time as you can gather from my question). So if you want to translate@
syntax into mundane calls to check how it works, you better comment it out temporarily first or you'd end up calling it twice and getting mumbojumbo results
– z33k
Mar 13 '18 at 14:50
|
show 5 more comments
I'd like to show an idea which is IMHO quite elegant. The solution proposed by t.dubrownik shows a pattern which is always the same: you need the three-layered wrapper regardless of what the decorator does.
So I thought this is a job for a meta-decorator, that is, a decorator for decorators. As a decorator is a function, it actually works as a regular decorator with arguments:
def parametrized(dec):
def layer(*args, **kwargs):
def repl(f):
return dec(f, *args, **kwargs)
return repl
return layer
This can be applied to a regular decorator in order to add parameters. So for instance, say we have the decorator which doubles the result of a function:
def double(f):
def aux(*xs, **kws):
return 2 * f(*xs, **kws)
return aux
@double
def function(a):
return 10 + a
print function(3) # Prints 26, namely 2 * (10 + 3)
With @parametrized
we can build a generic @multiply
decorator having a parameter
@parametrized
def multiply(f, n):
def aux(*xs, **kws):
return n * f(*xs, **kws)
return aux
@multiply(2)
def function(a):
return 10 + a
print function(3) # Prints 26
@multiply(3)
def function_again(a):
return 10 + a
print function(3) # Keeps printing 26
print function_again(3) # Prints 39, namely 3 * (10 + 3)
Conventionally the first parameter of a parametrized decorator is the function, while the remaining arguments will correspond to the parameter of the parametrized decorator.
An interesting usage example could be a type-safe assertive decorator:
import itertools as it
@parametrized
def types(f, *types):
def rep(*args):
for a, t, n in zip(args, types, it.count()):
if type(a) is not t:
raise TypeError('Value %d has not type %s. %s instead' %
(n, t, type(a))
)
return f(*args)
return rep
@types(str, int) # arg1 is str, arg2 is int
def string_multiply(text, times):
return text * times
print(string_multiply('hello', 3)) # Prints hellohellohello
print(string_multiply(3, 3)) # Fails miserably with TypeError
A final note: here I'm not using functools.wraps
for the wrapper functions, but I would recommend using it all the times.
I'd like to show an idea which is IMHO quite elegant. The solution proposed by t.dubrownik shows a pattern which is always the same: you need the three-layered wrapper regardless of what the decorator does.
So I thought this is a job for a meta-decorator, that is, a decorator for decorators. As a decorator is a function, it actually works as a regular decorator with arguments:
def parametrized(dec):
def layer(*args, **kwargs):
def repl(f):
return dec(f, *args, **kwargs)
return repl
return layer
This can be applied to a regular decorator in order to add parameters. So for instance, say we have the decorator which doubles the result of a function:
def double(f):
def aux(*xs, **kws):
return 2 * f(*xs, **kws)
return aux
@double
def function(a):
return 10 + a
print function(3) # Prints 26, namely 2 * (10 + 3)
With @parametrized
we can build a generic @multiply
decorator having a parameter
@parametrized
def multiply(f, n):
def aux(*xs, **kws):
return n * f(*xs, **kws)
return aux
@multiply(2)
def function(a):
return 10 + a
print function(3) # Prints 26
@multiply(3)
def function_again(a):
return 10 + a
print function(3) # Keeps printing 26
print function_again(3) # Prints 39, namely 3 * (10 + 3)
Conventionally the first parameter of a parametrized decorator is the function, while the remaining arguments will correspond to the parameter of the parametrized decorator.
An interesting usage example could be a type-safe assertive decorator:
import itertools as it
@parametrized
def types(f, *types):
def rep(*args):
for a, t, n in zip(args, types, it.count()):
if type(a) is not t:
raise TypeError('Value %d has not type %s. %s instead' %
(n, t, type(a))
)
return f(*args)
return rep
@types(str, int) # arg1 is str, arg2 is int
def string_multiply(text, times):
return text * times
print(string_multiply('hello', 3)) # Prints hellohellohello
print(string_multiply(3, 3)) # Fails miserably with TypeError
A final note: here I'm not using functools.wraps
for the wrapper functions, but I would recommend using it all the times.
edited May 20 '18 at 12:39
![](https://i.stack.imgur.com/RIZKi.png?s=32&g=1)
![](https://i.stack.imgur.com/RIZKi.png?s=32&g=1)
Peter Mortensen
13.6k1984111
13.6k1984111
answered Oct 1 '14 at 22:17
DacavDacav
6,96144469
6,96144469
Didn't use this exactly, but helped me get my head around the concept :) Thanks!
– mouckatron
Oct 10 '17 at 22:04
I tried this and had some issues.
– Jeff
Oct 14 '17 at 14:53
@Jeff could you share with us the kind of issues you had?
– Dacav
Oct 14 '17 at 16:49
I had it linked on my question, and I did figure it out... I needed to call@wraps
in mine for my particular case.
– Jeff
Oct 14 '17 at 19:33
3
Oh boy, I lost a whole day on this. Thankfully, I came around this answer (which incidentally could be the best answer ever created on the whole internet). They too use your@parametrized
trick. The problem I had was I forgot the@
syntax equals actual calls (somehow I knew that and didn't know that at the same time as you can gather from my question). So if you want to translate@
syntax into mundane calls to check how it works, you better comment it out temporarily first or you'd end up calling it twice and getting mumbojumbo results
– z33k
Mar 13 '18 at 14:50
|
show 5 more comments
Didn't use this exactly, but helped me get my head around the concept :) Thanks!
– mouckatron
Oct 10 '17 at 22:04
I tried this and had some issues.
– Jeff
Oct 14 '17 at 14:53
@Jeff could you share with us the kind of issues you had?
– Dacav
Oct 14 '17 at 16:49
I had it linked on my question, and I did figure it out... I needed to call@wraps
in mine for my particular case.
– Jeff
Oct 14 '17 at 19:33
3
Oh boy, I lost a whole day on this. Thankfully, I came around this answer (which incidentally could be the best answer ever created on the whole internet). They too use your@parametrized
trick. The problem I had was I forgot the@
syntax equals actual calls (somehow I knew that and didn't know that at the same time as you can gather from my question). So if you want to translate@
syntax into mundane calls to check how it works, you better comment it out temporarily first or you'd end up calling it twice and getting mumbojumbo results
– z33k
Mar 13 '18 at 14:50
Didn't use this exactly, but helped me get my head around the concept :) Thanks!
– mouckatron
Oct 10 '17 at 22:04
Didn't use this exactly, but helped me get my head around the concept :) Thanks!
– mouckatron
Oct 10 '17 at 22:04
I tried this and had some issues.
– Jeff
Oct 14 '17 at 14:53
I tried this and had some issues.
– Jeff
Oct 14 '17 at 14:53
@Jeff could you share with us the kind of issues you had?
– Dacav
Oct 14 '17 at 16:49
@Jeff could you share with us the kind of issues you had?
– Dacav
Oct 14 '17 at 16:49
I had it linked on my question, and I did figure it out... I needed to call
@wraps
in mine for my particular case.– Jeff
Oct 14 '17 at 19:33
I had it linked on my question, and I did figure it out... I needed to call
@wraps
in mine for my particular case.– Jeff
Oct 14 '17 at 19:33
3
3
Oh boy, I lost a whole day on this. Thankfully, I came around this answer (which incidentally could be the best answer ever created on the whole internet). They too use your
@parametrized
trick. The problem I had was I forgot the @
syntax equals actual calls (somehow I knew that and didn't know that at the same time as you can gather from my question). So if you want to translate @
syntax into mundane calls to check how it works, you better comment it out temporarily first or you'd end up calling it twice and getting mumbojumbo results– z33k
Mar 13 '18 at 14:50
Oh boy, I lost a whole day on this. Thankfully, I came around this answer (which incidentally could be the best answer ever created on the whole internet). They too use your
@parametrized
trick. The problem I had was I forgot the @
syntax equals actual calls (somehow I knew that and didn't know that at the same time as you can gather from my question). So if you want to translate @
syntax into mundane calls to check how it works, you better comment it out temporarily first or you'd end up calling it twice and getting mumbojumbo results– z33k
Mar 13 '18 at 14:50
|
show 5 more comments
Here is a slightly modified version of t.dubrownik's answer. Why?
- As a general template, you should return the return value from the original function.
- This changes the name of the function, which could affect other decorators / code.
So use @functools.wraps()
:
from functools import wraps
def decorator(argument):
def real_decorator(function):
@wraps(function)
def wrapper(*args, **kwargs):
funny_stuff()
something_with_argument(argument)
retval = function(*args, **kwargs)
more_funny_stuff()
return retval
return wrapper
return real_decorator
add a comment |
Here is a slightly modified version of t.dubrownik's answer. Why?
- As a general template, you should return the return value from the original function.
- This changes the name of the function, which could affect other decorators / code.
So use @functools.wraps()
:
from functools import wraps
def decorator(argument):
def real_decorator(function):
@wraps(function)
def wrapper(*args, **kwargs):
funny_stuff()
something_with_argument(argument)
retval = function(*args, **kwargs)
more_funny_stuff()
return retval
return wrapper
return real_decorator
add a comment |
Here is a slightly modified version of t.dubrownik's answer. Why?
- As a general template, you should return the return value from the original function.
- This changes the name of the function, which could affect other decorators / code.
So use @functools.wraps()
:
from functools import wraps
def decorator(argument):
def real_decorator(function):
@wraps(function)
def wrapper(*args, **kwargs):
funny_stuff()
something_with_argument(argument)
retval = function(*args, **kwargs)
more_funny_stuff()
return retval
return wrapper
return real_decorator
Here is a slightly modified version of t.dubrownik's answer. Why?
- As a general template, you should return the return value from the original function.
- This changes the name of the function, which could affect other decorators / code.
So use @functools.wraps()
:
from functools import wraps
def decorator(argument):
def real_decorator(function):
@wraps(function)
def wrapper(*args, **kwargs):
funny_stuff()
something_with_argument(argument)
retval = function(*args, **kwargs)
more_funny_stuff()
return retval
return wrapper
return real_decorator
edited Jun 22 '18 at 10:44
![](https://i.stack.imgur.com/eKpAk.png?s=32&g=1)
![](https://i.stack.imgur.com/eKpAk.png?s=32&g=1)
fedorqui
167k53341381
167k53341381
answered Mar 3 '17 at 14:11
![](https://i.stack.imgur.com/E6d3D.jpg?s=32&g=1)
![](https://i.stack.imgur.com/E6d3D.jpg?s=32&g=1)
Ross RRoss R
2,46742124
2,46742124
add a comment |
add a comment |
I presume your problem is passing arguments to your decorator. This is a little tricky and not straightforward.
Here's an example of how to do this:
class MyDec(object):
def __init__(self,flag):
self.flag = flag
def __call__(self, original_func):
decorator_self = self
def wrappee( *args, **kwargs):
print 'in decorator before wrapee with flag ',decorator_self.flag
original_func(*args,**kwargs)
print 'in decorator after wrapee with flag ',decorator_self.flag
return wrappee
@MyDec('foo de fa fa')
def bar(a,b,c):
print 'in bar',a,b,c
bar('x','y','z')
Prints:
in decorator before wrapee with flag foo de fa fa
in bar x y z
in decorator after wrapee with flag foo de fa fa
See Bruce Eckel's article for more details.
19
Beware of decorator classes. They don't work on methods unless you manually reinvent the logic of instancemethod descriptors.
– user395760
May 8 '11 at 18:01
9
delnan, care to elaborate? I've only had to use this pattern once, so I haven't hit any of the pitfalls yet.
– Ross Rogers
May 8 '11 at 18:04
2
@RossRogers My guess is that @delnan is referring to things like__name__
which an instance of the decorator class won't have?
– jamesc
Jan 13 '14 at 17:18
9
@jamesc That too, though that's relatively easy to solve. The specific case I was referring to wasclass Foo: @MyDec(...) def method(self, ...): blah
which does not work becauseFoo().method
won't be a bound method and won't passself
automatically. This too can be fixed, by makingMyDec
a descriptor and creating bound methods in__get__
, but it's more involved and much less obvious. In the end, decorator classes are not as convenient as they seem.
– user395760
Jan 13 '14 at 21:49
2
@delnan I'd like to see this caveat featured more prominently. I'm hitting it and am interested in seeing a solution that DOES work (more involved an less obvious though it may be).
– HaPsantran
Mar 13 '16 at 6:42
|
show 1 more comment
I presume your problem is passing arguments to your decorator. This is a little tricky and not straightforward.
Here's an example of how to do this:
class MyDec(object):
def __init__(self,flag):
self.flag = flag
def __call__(self, original_func):
decorator_self = self
def wrappee( *args, **kwargs):
print 'in decorator before wrapee with flag ',decorator_self.flag
original_func(*args,**kwargs)
print 'in decorator after wrapee with flag ',decorator_self.flag
return wrappee
@MyDec('foo de fa fa')
def bar(a,b,c):
print 'in bar',a,b,c
bar('x','y','z')
Prints:
in decorator before wrapee with flag foo de fa fa
in bar x y z
in decorator after wrapee with flag foo de fa fa
See Bruce Eckel's article for more details.
19
Beware of decorator classes. They don't work on methods unless you manually reinvent the logic of instancemethod descriptors.
– user395760
May 8 '11 at 18:01
9
delnan, care to elaborate? I've only had to use this pattern once, so I haven't hit any of the pitfalls yet.
– Ross Rogers
May 8 '11 at 18:04
2
@RossRogers My guess is that @delnan is referring to things like__name__
which an instance of the decorator class won't have?
– jamesc
Jan 13 '14 at 17:18
9
@jamesc That too, though that's relatively easy to solve. The specific case I was referring to wasclass Foo: @MyDec(...) def method(self, ...): blah
which does not work becauseFoo().method
won't be a bound method and won't passself
automatically. This too can be fixed, by makingMyDec
a descriptor and creating bound methods in__get__
, but it's more involved and much less obvious. In the end, decorator classes are not as convenient as they seem.
– user395760
Jan 13 '14 at 21:49
2
@delnan I'd like to see this caveat featured more prominently. I'm hitting it and am interested in seeing a solution that DOES work (more involved an less obvious though it may be).
– HaPsantran
Mar 13 '16 at 6:42
|
show 1 more comment
I presume your problem is passing arguments to your decorator. This is a little tricky and not straightforward.
Here's an example of how to do this:
class MyDec(object):
def __init__(self,flag):
self.flag = flag
def __call__(self, original_func):
decorator_self = self
def wrappee( *args, **kwargs):
print 'in decorator before wrapee with flag ',decorator_self.flag
original_func(*args,**kwargs)
print 'in decorator after wrapee with flag ',decorator_self.flag
return wrappee
@MyDec('foo de fa fa')
def bar(a,b,c):
print 'in bar',a,b,c
bar('x','y','z')
Prints:
in decorator before wrapee with flag foo de fa fa
in bar x y z
in decorator after wrapee with flag foo de fa fa
See Bruce Eckel's article for more details.
I presume your problem is passing arguments to your decorator. This is a little tricky and not straightforward.
Here's an example of how to do this:
class MyDec(object):
def __init__(self,flag):
self.flag = flag
def __call__(self, original_func):
decorator_self = self
def wrappee( *args, **kwargs):
print 'in decorator before wrapee with flag ',decorator_self.flag
original_func(*args,**kwargs)
print 'in decorator after wrapee with flag ',decorator_self.flag
return wrappee
@MyDec('foo de fa fa')
def bar(a,b,c):
print 'in bar',a,b,c
bar('x','y','z')
Prints:
in decorator before wrapee with flag foo de fa fa
in bar x y z
in decorator after wrapee with flag foo de fa fa
See Bruce Eckel's article for more details.
edited May 20 '18 at 12:37
![](https://i.stack.imgur.com/RIZKi.png?s=32&g=1)
![](https://i.stack.imgur.com/RIZKi.png?s=32&g=1)
Peter Mortensen
13.6k1984111
13.6k1984111
answered May 8 '11 at 17:56
![](https://i.stack.imgur.com/dUQnr.png?s=32&g=1)
![](https://i.stack.imgur.com/dUQnr.png?s=32&g=1)
Ross RogersRoss Rogers
11.7k1683138
11.7k1683138
19
Beware of decorator classes. They don't work on methods unless you manually reinvent the logic of instancemethod descriptors.
– user395760
May 8 '11 at 18:01
9
delnan, care to elaborate? I've only had to use this pattern once, so I haven't hit any of the pitfalls yet.
– Ross Rogers
May 8 '11 at 18:04
2
@RossRogers My guess is that @delnan is referring to things like__name__
which an instance of the decorator class won't have?
– jamesc
Jan 13 '14 at 17:18
9
@jamesc That too, though that's relatively easy to solve. The specific case I was referring to wasclass Foo: @MyDec(...) def method(self, ...): blah
which does not work becauseFoo().method
won't be a bound method and won't passself
automatically. This too can be fixed, by makingMyDec
a descriptor and creating bound methods in__get__
, but it's more involved and much less obvious. In the end, decorator classes are not as convenient as they seem.
– user395760
Jan 13 '14 at 21:49
2
@delnan I'd like to see this caveat featured more prominently. I'm hitting it and am interested in seeing a solution that DOES work (more involved an less obvious though it may be).
– HaPsantran
Mar 13 '16 at 6:42
|
show 1 more comment
19
Beware of decorator classes. They don't work on methods unless you manually reinvent the logic of instancemethod descriptors.
– user395760
May 8 '11 at 18:01
9
delnan, care to elaborate? I've only had to use this pattern once, so I haven't hit any of the pitfalls yet.
– Ross Rogers
May 8 '11 at 18:04
2
@RossRogers My guess is that @delnan is referring to things like__name__
which an instance of the decorator class won't have?
– jamesc
Jan 13 '14 at 17:18
9
@jamesc That too, though that's relatively easy to solve. The specific case I was referring to wasclass Foo: @MyDec(...) def method(self, ...): blah
which does not work becauseFoo().method
won't be a bound method and won't passself
automatically. This too can be fixed, by makingMyDec
a descriptor and creating bound methods in__get__
, but it's more involved and much less obvious. In the end, decorator classes are not as convenient as they seem.
– user395760
Jan 13 '14 at 21:49
2
@delnan I'd like to see this caveat featured more prominently. I'm hitting it and am interested in seeing a solution that DOES work (more involved an less obvious though it may be).
– HaPsantran
Mar 13 '16 at 6:42
19
19
Beware of decorator classes. They don't work on methods unless you manually reinvent the logic of instancemethod descriptors.
– user395760
May 8 '11 at 18:01
Beware of decorator classes. They don't work on methods unless you manually reinvent the logic of instancemethod descriptors.
– user395760
May 8 '11 at 18:01
9
9
delnan, care to elaborate? I've only had to use this pattern once, so I haven't hit any of the pitfalls yet.
– Ross Rogers
May 8 '11 at 18:04
delnan, care to elaborate? I've only had to use this pattern once, so I haven't hit any of the pitfalls yet.
– Ross Rogers
May 8 '11 at 18:04
2
2
@RossRogers My guess is that @delnan is referring to things like
__name__
which an instance of the decorator class won't have?– jamesc
Jan 13 '14 at 17:18
@RossRogers My guess is that @delnan is referring to things like
__name__
which an instance of the decorator class won't have?– jamesc
Jan 13 '14 at 17:18
9
9
@jamesc That too, though that's relatively easy to solve. The specific case I was referring to was
class Foo: @MyDec(...) def method(self, ...): blah
which does not work because Foo().method
won't be a bound method and won't pass self
automatically. This too can be fixed, by making MyDec
a descriptor and creating bound methods in __get__
, but it's more involved and much less obvious. In the end, decorator classes are not as convenient as they seem.– user395760
Jan 13 '14 at 21:49
@jamesc That too, though that's relatively easy to solve. The specific case I was referring to was
class Foo: @MyDec(...) def method(self, ...): blah
which does not work because Foo().method
won't be a bound method and won't pass self
automatically. This too can be fixed, by making MyDec
a descriptor and creating bound methods in __get__
, but it's more involved and much less obvious. In the end, decorator classes are not as convenient as they seem.– user395760
Jan 13 '14 at 21:49
2
2
@delnan I'd like to see this caveat featured more prominently. I'm hitting it and am interested in seeing a solution that DOES work (more involved an less obvious though it may be).
– HaPsantran
Mar 13 '16 at 6:42
@delnan I'd like to see this caveat featured more prominently. I'm hitting it and am interested in seeing a solution that DOES work (more involved an less obvious though it may be).
– HaPsantran
Mar 13 '16 at 6:42
|
show 1 more comment
In my instance, I decided to solve this via a one-line lambda to create a new decorator function:
def finished_message(function, message="Finished!"):
def wrapper(*args, **kwargs):
output = function(*args,**kwargs)
print(message)
return output
return wrapper
@finished_message
def func():
pass
my_finished_message = lambda f: finished_message(f, "All Done!")
@my_finished_message
def my_func():
pass
if __name__ == '__main__':
func()
my_func()
When executed, this prints:
Finished!
All Done!
Perhaps not as extensible as other solutions, but worked for me.
This works. Although yes, this makes it hard to set the value to the decorator.
– Arindam Roychowdhury
Jan 11 at 11:28
add a comment |
In my instance, I decided to solve this via a one-line lambda to create a new decorator function:
def finished_message(function, message="Finished!"):
def wrapper(*args, **kwargs):
output = function(*args,**kwargs)
print(message)
return output
return wrapper
@finished_message
def func():
pass
my_finished_message = lambda f: finished_message(f, "All Done!")
@my_finished_message
def my_func():
pass
if __name__ == '__main__':
func()
my_func()
When executed, this prints:
Finished!
All Done!
Perhaps not as extensible as other solutions, but worked for me.
This works. Although yes, this makes it hard to set the value to the decorator.
– Arindam Roychowdhury
Jan 11 at 11:28
add a comment |
In my instance, I decided to solve this via a one-line lambda to create a new decorator function:
def finished_message(function, message="Finished!"):
def wrapper(*args, **kwargs):
output = function(*args,**kwargs)
print(message)
return output
return wrapper
@finished_message
def func():
pass
my_finished_message = lambda f: finished_message(f, "All Done!")
@my_finished_message
def my_func():
pass
if __name__ == '__main__':
func()
my_func()
When executed, this prints:
Finished!
All Done!
Perhaps not as extensible as other solutions, but worked for me.
In my instance, I decided to solve this via a one-line lambda to create a new decorator function:
def finished_message(function, message="Finished!"):
def wrapper(*args, **kwargs):
output = function(*args,**kwargs)
print(message)
return output
return wrapper
@finished_message
def func():
pass
my_finished_message = lambda f: finished_message(f, "All Done!")
@my_finished_message
def my_func():
pass
if __name__ == '__main__':
func()
my_func()
When executed, this prints:
Finished!
All Done!
Perhaps not as extensible as other solutions, but worked for me.
answered Jun 15 '18 at 19:21
ZacBookZacBook
111
111
This works. Although yes, this makes it hard to set the value to the decorator.
– Arindam Roychowdhury
Jan 11 at 11:28
add a comment |
This works. Although yes, this makes it hard to set the value to the decorator.
– Arindam Roychowdhury
Jan 11 at 11:28
This works. Although yes, this makes it hard to set the value to the decorator.
– Arindam Roychowdhury
Jan 11 at 11:28
This works. Although yes, this makes it hard to set the value to the decorator.
– Arindam Roychowdhury
Jan 11 at 11:28
add a comment |
define this "decoratorize function" to generate customized decorator function:
def decoratorize(FUN, **kw):
def foo(*args, **kws):
return FUN(*args, **kws, **kw)
return foo
use it this way:
@decoratorize(FUN, arg1 = , arg2 = , ...)
def bar(...):
...
add a comment |
define this "decoratorize function" to generate customized decorator function:
def decoratorize(FUN, **kw):
def foo(*args, **kws):
return FUN(*args, **kws, **kw)
return foo
use it this way:
@decoratorize(FUN, arg1 = , arg2 = , ...)
def bar(...):
...
add a comment |
define this "decoratorize function" to generate customized decorator function:
def decoratorize(FUN, **kw):
def foo(*args, **kws):
return FUN(*args, **kws, **kw)
return foo
use it this way:
@decoratorize(FUN, arg1 = , arg2 = , ...)
def bar(...):
...
define this "decoratorize function" to generate customized decorator function:
def decoratorize(FUN, **kw):
def foo(*args, **kws):
return FUN(*args, **kws, **kw)
return foo
use it this way:
@decoratorize(FUN, arg1 = , arg2 = , ...)
def bar(...):
...
answered Oct 30 '18 at 5:40
chen.wqchen.wq
62
62
add a comment |
add a comment |
def decorator(argument):
def real_decorator(function):
def wrapper(*args):
for arg in args:
assert type(arg)==int,f'{arg} is not an interger'
result = function(*args)
result = result*argument
return result
return wrapper
return real_decorator
Usage of the decorator
@decorator(2)
def adder(*args):
sum=0
for i in args:
sum+=i
return sum
Then the
adder(2,3)
produces
10
but
adder('hi',3)
produces
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
<ipython-input-143-242a8feb1cc4> in <module>
----> 1 adder('hi',3)
<ipython-input-140-d3420c248ebd> in wrapper(*args)
3 def wrapper(*args):
4 for arg in args:
----> 5 assert type(arg)==int,f'{arg} is not an interger'
6 result = function(*args)
7 result = result*argument
AssertionError: hi is not an interger
add a comment |
def decorator(argument):
def real_decorator(function):
def wrapper(*args):
for arg in args:
assert type(arg)==int,f'{arg} is not an interger'
result = function(*args)
result = result*argument
return result
return wrapper
return real_decorator
Usage of the decorator
@decorator(2)
def adder(*args):
sum=0
for i in args:
sum+=i
return sum
Then the
adder(2,3)
produces
10
but
adder('hi',3)
produces
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
<ipython-input-143-242a8feb1cc4> in <module>
----> 1 adder('hi',3)
<ipython-input-140-d3420c248ebd> in wrapper(*args)
3 def wrapper(*args):
4 for arg in args:
----> 5 assert type(arg)==int,f'{arg} is not an interger'
6 result = function(*args)
7 result = result*argument
AssertionError: hi is not an interger
add a comment |
def decorator(argument):
def real_decorator(function):
def wrapper(*args):
for arg in args:
assert type(arg)==int,f'{arg} is not an interger'
result = function(*args)
result = result*argument
return result
return wrapper
return real_decorator
Usage of the decorator
@decorator(2)
def adder(*args):
sum=0
for i in args:
sum+=i
return sum
Then the
adder(2,3)
produces
10
but
adder('hi',3)
produces
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
<ipython-input-143-242a8feb1cc4> in <module>
----> 1 adder('hi',3)
<ipython-input-140-d3420c248ebd> in wrapper(*args)
3 def wrapper(*args):
4 for arg in args:
----> 5 assert type(arg)==int,f'{arg} is not an interger'
6 result = function(*args)
7 result = result*argument
AssertionError: hi is not an interger
def decorator(argument):
def real_decorator(function):
def wrapper(*args):
for arg in args:
assert type(arg)==int,f'{arg} is not an interger'
result = function(*args)
result = result*argument
return result
return wrapper
return real_decorator
Usage of the decorator
@decorator(2)
def adder(*args):
sum=0
for i in args:
sum+=i
return sum
Then the
adder(2,3)
produces
10
but
adder('hi',3)
produces
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
<ipython-input-143-242a8feb1cc4> in <module>
----> 1 adder('hi',3)
<ipython-input-140-d3420c248ebd> in wrapper(*args)
3 def wrapper(*args):
4 for arg in args:
----> 5 assert type(arg)==int,f'{arg} is not an interger'
6 result = function(*args)
7 result = result*argument
AssertionError: hi is not an interger
answered Dec 29 '18 at 21:57
![](https://lh4.googleusercontent.com/-oMOywLnvOh8/AAAAAAAAAAI/AAAAAAAARxc/ynEEMOSyeEU/photo.jpg?sz=32)
![](https://lh4.googleusercontent.com/-oMOywLnvOh8/AAAAAAAAAAI/AAAAAAAARxc/ynEEMOSyeEU/photo.jpg?sz=32)
Gajendra D AmbiGajendra D Ambi
9811115
9811115
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f5929107%2fdecorators-with-parameters%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
eRSCy4yt5HfGSPhXoLCaqg6sykQq9p T60aOsN LGN
2
Your example is not syntactically valid.
execute_complete_reservation
takes two parameters, but you're passing it one. Decorators are just syntactic sugar for wrapping functions inside other functions. See docs.python.org/reference/compound_stmts.html#function for complete documentation.– Brian Clapper
May 8 '11 at 17:50