How do I pass a class field to a decorator on a class method as an argument? What I want to do is something like:
class Client(object):
def __init__(self, url):
self.url = url
#check_authorization("some_attr", self.url)
def get(self):
do_work()
It complains that self does not exist for passing self.url to the decorator. Is there a way around this?
Yes. Instead of passing in the instance attribute at class definition time, check it at runtime:
def check_authorization(f):
def wrapper(*args):
print args[0].url
return f(*args)
return wrapper
class Client(object):
def __init__(self, url):
self.url = url
#check_authorization
def get(self):
print 'get'
>>> Client('http://www.google.com').get()
http://www.google.com
get
The decorator intercepts the method arguments; the first argument is the instance, so it reads the attribute off of that. You can pass in the attribute name as a string to the decorator and use getattr if you don't want to hardcode the attribute name:
def check_authorization(attribute):
def _check_authorization(f):
def wrapper(self, *args):
print getattr(self, attribute)
return f(self, *args)
return wrapper
return _check_authorization
A more concise example might be as follows:
#/usr/bin/env python3
from functools import wraps
def wrapper(method):
#wraps(method)
def _impl(self, *method_args, **method_kwargs):
method_output = method(self, *method_args, **method_kwargs)
return method_output + "!"
return _impl
class Foo:
#wrapper
def bar(self, word):
return word
f = Foo()
result = f.bar("kitty")
print(result)
Which will print:
kitty!
from re import search
from functools import wraps
def is_match(_lambda, pattern):
def wrapper(f):
#wraps(f)
def wrapped(self, *f_args, **f_kwargs):
if callable(_lambda) and search(pattern, (_lambda(self) or '')):
f(self, *f_args, **f_kwargs)
return wrapped
return wrapper
class MyTest(object):
def __init__(self):
self.name = 'foo'
self.surname = 'bar'
#is_match(lambda x: x.name, 'foo')
#is_match(lambda x: x.surname, 'foo')
def my_rule(self):
print 'my_rule : ok'
#is_match(lambda x: x.name, 'foo')
#is_match(lambda x: x.surname, 'bar')
def my_rule2(self):
print 'my_rule2 : ok'
test = MyTest()
test.my_rule()
test.my_rule2()
ouput:
my_rule2 : ok
Another option would be to abandon the syntactic sugar and decorate in the __init__ of the class.
def countdown(number):
def countdown_decorator(func):
def func_wrapper():
for index in reversed(range(1, number+1)):
print(index)
func()
return func_wrapper
return countdown_decorator
class MySuperClass():
def __init__(self, number):
self.number = number
self.do_thing = countdown(number)(self.do_thing)
def do_thing(self):
print('im doing stuff!')
myclass = MySuperClass(3)
myclass.do_thing()
which would print
3
2
1
im doing stuff!
I know this issue is quite old, but the below workaround hasn't been proposed before. The problem here is that you can't access self in a class block, but you can in a class method.
Let's create a dummy decorator to repeat a function some times.
import functools
def repeat(num_rep):
def decorator_repeat(func):
#functools.wraps(func)
def wrapper_repeat(*args, **kwargs):
for _ in range(num_rep):
value = func(*args, **kwargs)
return
return wrapper_repeat
return decorator_repeat
class A:
def __init__(self, times, name):
self.times = times
self.name = name
def get_name(self):
#repeat(num_rep=self.times)
def _get_name():
print(f'Hi {self.name}')
_get_name()
I know this is an old question, but this solution has not been mentioned yet, hopefully it may help someone even today, after 8 years.
So, what about wrapping a wrapper? Let's assume one cannot change the decorator neither decorate those methods in init (they may be #property decorated or whatever). There is always a possibility to create custom, class-specific decorator that will capture self and subsequently call the original decorator, passing runtime attribute to it.
Here is a working example (f-strings require python 3.6):
import functools
# imagine this is at some different place and cannot be changed
def check_authorization(some_attr, url):
def decorator(func):
#functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"checking authorization for '{url}'...")
return func(*args, **kwargs)
return wrapper
return decorator
# another dummy function to make the example work
def do_work():
print("work is done...")
###################
# wrapped wrapper #
###################
def custom_check_authorization(some_attr):
def decorator(func):
# assuming this will be used only on this particular class
#functools.wraps(func)
def wrapper(self, *args, **kwargs):
# get url
url = self.url
# decorate function with original decorator, pass url
return check_authorization(some_attr, url)(func)(self, *args, **kwargs)
return wrapper
return decorator
#############################
# original example, updated #
#############################
class Client(object):
def __init__(self, url):
self.url = url
#custom_check_authorization("some_attr")
def get(self):
do_work()
# create object
client = Client(r"https://stackoverflow.com/questions/11731136/class-method-decorator-with-self-arguments")
# call decorated function
client.get()
output:
checking authorisation for 'https://stackoverflow.com/questions/11731136/class-method-decorator-with-self-arguments'...
work is done...
You can't. There's no self in the class body, because no instance exists. You'd need to pass it, say, a str containing the attribute name to lookup on the instance, which the returned function can then do, or use a different method entirely.
It will be very useful to have a general-purpose utility, that can turn any decorator for functions, into decorator for methods. I thought about it for an hour, and actually come up with one:
from typing import Callable
Decorator = Callable[[Callable], Callable]
def decorate_method(dec_for_function: Decorator) -> Decorator:
def dec_for_method(unbounded_method) -> Callable:
# here, `unbounded_method` will be a unbounded function, whose
# invokation must have its first arg as a valid `self`. When it
# return, it also must return an unbounded method.
def decorated_unbounded_method(self, *args, **kwargs):
#dec_for_function
def bounded_method(*args, **kwargs):
return unbounded_method(self, *args, **kwargs)
return bounded_method(*args, **kwargs)
return decorated_unbounded_method
return dec_for_method
The usage is:
# for any decorator (with or without arguments)
#some_decorator_with_arguments(1, 2, 3)
def xyz(...): ...
# use it on a method:
class ABC:
#decorate_method(some_decorator_with_arguments(1, 2, 3))
def xyz(self, ...): ...
Test:
def dec_for_add(fn):
"""This decorator expects a function: (x,y) -> int.
If you use it on a method (self, x, y) -> int, it will fail at runtime.
"""
print(f"decorating: {fn}")
def add_fn(x,y):
print(f"Adding {x} + {y} by using {fn}")
return fn(x,y)
return add_fn
#dec_for_add
def add(x,y):
return x+y
add(1,2) # OK!
class A:
#dec_for_add
def f(self, x, y):
# ensure `self` is still a valid instance
assert isinstance(self, A)
return x+y
# TypeError: add_fn() takes 2 positional arguments but 3 were given
# A().f(1,2)
class A:
#decorate_method(dec_for_add)
def f(self, x, y):
# ensure `self` is still a valid instance
assert isinstance(self, A)
return x+y
# Now works!!
A().f(1,2)
Related
I have created a decorator which I am using to manage logging. I want logging to occur before and after the decorated function runs. The function works fine when interacting with very basic functions, however, when interacting with methods that are a part of other classes, things break. I suspect the issue is a result of there being 2 self arguments. Do you have any idea how to resolve it?
Simplified Decorator Class
class Logger:
def __init__(self, logging_type:str = 'debug'):
self.logging_type = logging_type
def __call__(self, decorated_function:callable):
self.func = decorated_function
return getattr(self, self.logging_type)
def debug(self, *args, **kwargs):
print("starting function")
output = self.func(*args, **kwargs)
print("Completing Function")
return output
We see that the decorator works on basic functions:
#Logger(logging_type="debug")
def simple_function(x):
return x**2
In [2]: simple_function(3)
starting function
Completing Function
Out[2]: 9
However, fails when work with other classes:
class BigClass:
def __init__(self, stuff = 10):
self.stuff = stuff
#Logger(logging_type="debug")
def cool_function(self, input1: int):
return self.stuff + input1
In [16]: test = BigClass()
...: test.cool_function(3)
starting function
It then hits a type error on the output line:
TypeError: cool_function() missing 1 required positional argument: 'input1'
Ideas?
By all means read juanpa.arrivillaga's informative answer. But here is a simpler approach. In writing a class decorator of this type, __call__ should return an ordinary function instead of a member function, like this:
class Logger:
def __init__(self, logging_type:str = 'debug'):
self.logging_function = getattr(self, logging_type)
def __call__(self, decorated_function: callable):
def f(*args, **kwargs):
return self.logging_function(decorated_function, *args, **kwargs)
return f
def debug(self, decorated_function, *args, **kwargs):
print("starting function")
output = decorated_function(*args, **kwargs)
print("Completing Function")
return output
#Logger(logging_type="debug")
def simple_function(x):
return x**2
class BigClass:
def __init__(self, stuff = 10):
self.stuff = stuff
#Logger(logging_type="debug")
def cool_function(self, input1: int):
return self.stuff + input1
print(simple_function(12))
test = BigClass()
print(test.cool_function(3))
OUTPUT:
starting function
Completing Function
144
starting function
Completing Function
13
The problem is that you are decorating your function with a bound-method type, look at type(BigClass.cool_function), you'll see something like: <bound method Logger.debug of <__main__.Logger object at 0x11081f7c0>. Since bound-method objects aren't functions, they don't implement the descriptor protocol to bind the instance as the first argument, hence, the instance is never passed implicitly as the first argument.
The best solution is to avoid class-based decorators to begin with. Here's how you could implement what you are doing using function-based decorators, using the closures to maintain internal state:
from functools import wraps
def logger(*, logging_type): # I prefer keyword-only arugments for decorators, but that is your call...
def decorator(func):
#wraps(func)
def debug(*args, **kwargs):
print("starting function")
result = func(*args, **kwargs)
print("ending function")
return result
#wraps(func)
def another_option(*args, **kwargs):
print("another option")
return func(*args, **kwargs)
options = {"debug": debug, "another_option": another_option}
return options[logging_type]
return decorator
class BigClass:
def __init__(self, stuff = 10):
self.stuff = stuff
#logger(logging_type="debug")
def cool_function(self, input1: int):
return self.stuff + input1
#logger(logging_type="another_option")
def another_function(self):
return self.stuff*100
How do I pass a class field to a decorator on a class method as an argument? What I want to do is something like:
class Client(object):
def __init__(self, url):
self.url = url
#check_authorization("some_attr", self.url)
def get(self):
do_work()
It complains that self does not exist for passing self.url to the decorator. Is there a way around this?
Yes. Instead of passing in the instance attribute at class definition time, check it at runtime:
def check_authorization(f):
def wrapper(*args):
print args[0].url
return f(*args)
return wrapper
class Client(object):
def __init__(self, url):
self.url = url
#check_authorization
def get(self):
print 'get'
>>> Client('http://www.google.com').get()
http://www.google.com
get
The decorator intercepts the method arguments; the first argument is the instance, so it reads the attribute off of that. You can pass in the attribute name as a string to the decorator and use getattr if you don't want to hardcode the attribute name:
def check_authorization(attribute):
def _check_authorization(f):
def wrapper(self, *args):
print getattr(self, attribute)
return f(self, *args)
return wrapper
return _check_authorization
A more concise example might be as follows:
#/usr/bin/env python3
from functools import wraps
def wrapper(method):
#wraps(method)
def _impl(self, *method_args, **method_kwargs):
method_output = method(self, *method_args, **method_kwargs)
return method_output + "!"
return _impl
class Foo:
#wrapper
def bar(self, word):
return word
f = Foo()
result = f.bar("kitty")
print(result)
Which will print:
kitty!
from re import search
from functools import wraps
def is_match(_lambda, pattern):
def wrapper(f):
#wraps(f)
def wrapped(self, *f_args, **f_kwargs):
if callable(_lambda) and search(pattern, (_lambda(self) or '')):
f(self, *f_args, **f_kwargs)
return wrapped
return wrapper
class MyTest(object):
def __init__(self):
self.name = 'foo'
self.surname = 'bar'
#is_match(lambda x: x.name, 'foo')
#is_match(lambda x: x.surname, 'foo')
def my_rule(self):
print 'my_rule : ok'
#is_match(lambda x: x.name, 'foo')
#is_match(lambda x: x.surname, 'bar')
def my_rule2(self):
print 'my_rule2 : ok'
test = MyTest()
test.my_rule()
test.my_rule2()
ouput:
my_rule2 : ok
Another option would be to abandon the syntactic sugar and decorate in the __init__ of the class.
def countdown(number):
def countdown_decorator(func):
def func_wrapper():
for index in reversed(range(1, number+1)):
print(index)
func()
return func_wrapper
return countdown_decorator
class MySuperClass():
def __init__(self, number):
self.number = number
self.do_thing = countdown(number)(self.do_thing)
def do_thing(self):
print('im doing stuff!')
myclass = MySuperClass(3)
myclass.do_thing()
which would print
3
2
1
im doing stuff!
I know this issue is quite old, but the below workaround hasn't been proposed before. The problem here is that you can't access self in a class block, but you can in a class method.
Let's create a dummy decorator to repeat a function some times.
import functools
def repeat(num_rep):
def decorator_repeat(func):
#functools.wraps(func)
def wrapper_repeat(*args, **kwargs):
for _ in range(num_rep):
value = func(*args, **kwargs)
return
return wrapper_repeat
return decorator_repeat
class A:
def __init__(self, times, name):
self.times = times
self.name = name
def get_name(self):
#repeat(num_rep=self.times)
def _get_name():
print(f'Hi {self.name}')
_get_name()
I know this is an old question, but this solution has not been mentioned yet, hopefully it may help someone even today, after 8 years.
So, what about wrapping a wrapper? Let's assume one cannot change the decorator neither decorate those methods in init (they may be #property decorated or whatever). There is always a possibility to create custom, class-specific decorator that will capture self and subsequently call the original decorator, passing runtime attribute to it.
Here is a working example (f-strings require python 3.6):
import functools
# imagine this is at some different place and cannot be changed
def check_authorization(some_attr, url):
def decorator(func):
#functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"checking authorization for '{url}'...")
return func(*args, **kwargs)
return wrapper
return decorator
# another dummy function to make the example work
def do_work():
print("work is done...")
###################
# wrapped wrapper #
###################
def custom_check_authorization(some_attr):
def decorator(func):
# assuming this will be used only on this particular class
#functools.wraps(func)
def wrapper(self, *args, **kwargs):
# get url
url = self.url
# decorate function with original decorator, pass url
return check_authorization(some_attr, url)(func)(self, *args, **kwargs)
return wrapper
return decorator
#############################
# original example, updated #
#############################
class Client(object):
def __init__(self, url):
self.url = url
#custom_check_authorization("some_attr")
def get(self):
do_work()
# create object
client = Client(r"https://stackoverflow.com/questions/11731136/class-method-decorator-with-self-arguments")
# call decorated function
client.get()
output:
checking authorisation for 'https://stackoverflow.com/questions/11731136/class-method-decorator-with-self-arguments'...
work is done...
You can't. There's no self in the class body, because no instance exists. You'd need to pass it, say, a str containing the attribute name to lookup on the instance, which the returned function can then do, or use a different method entirely.
It will be very useful to have a general-purpose utility, that can turn any decorator for functions, into decorator for methods. I thought about it for an hour, and actually come up with one:
from typing import Callable
Decorator = Callable[[Callable], Callable]
def decorate_method(dec_for_function: Decorator) -> Decorator:
def dec_for_method(unbounded_method) -> Callable:
# here, `unbounded_method` will be a unbounded function, whose
# invokation must have its first arg as a valid `self`. When it
# return, it also must return an unbounded method.
def decorated_unbounded_method(self, *args, **kwargs):
#dec_for_function
def bounded_method(*args, **kwargs):
return unbounded_method(self, *args, **kwargs)
return bounded_method(*args, **kwargs)
return decorated_unbounded_method
return dec_for_method
The usage is:
# for any decorator (with or without arguments)
#some_decorator_with_arguments(1, 2, 3)
def xyz(...): ...
# use it on a method:
class ABC:
#decorate_method(some_decorator_with_arguments(1, 2, 3))
def xyz(self, ...): ...
Test:
def dec_for_add(fn):
"""This decorator expects a function: (x,y) -> int.
If you use it on a method (self, x, y) -> int, it will fail at runtime.
"""
print(f"decorating: {fn}")
def add_fn(x,y):
print(f"Adding {x} + {y} by using {fn}")
return fn(x,y)
return add_fn
#dec_for_add
def add(x,y):
return x+y
add(1,2) # OK!
class A:
#dec_for_add
def f(self, x, y):
# ensure `self` is still a valid instance
assert isinstance(self, A)
return x+y
# TypeError: add_fn() takes 2 positional arguments but 3 were given
# A().f(1,2)
class A:
#decorate_method(dec_for_add)
def f(self, x, y):
# ensure `self` is still a valid instance
assert isinstance(self, A)
return x+y
# Now works!!
A().f(1,2)
How do I pass a class field to a decorator on a class method as an argument? What I want to do is something like:
class Client(object):
def __init__(self, url):
self.url = url
#check_authorization("some_attr", self.url)
def get(self):
do_work()
It complains that self does not exist for passing self.url to the decorator. Is there a way around this?
Yes. Instead of passing in the instance attribute at class definition time, check it at runtime:
def check_authorization(f):
def wrapper(*args):
print args[0].url
return f(*args)
return wrapper
class Client(object):
def __init__(self, url):
self.url = url
#check_authorization
def get(self):
print 'get'
>>> Client('http://www.google.com').get()
http://www.google.com
get
The decorator intercepts the method arguments; the first argument is the instance, so it reads the attribute off of that. You can pass in the attribute name as a string to the decorator and use getattr if you don't want to hardcode the attribute name:
def check_authorization(attribute):
def _check_authorization(f):
def wrapper(self, *args):
print getattr(self, attribute)
return f(self, *args)
return wrapper
return _check_authorization
A more concise example might be as follows:
#/usr/bin/env python3
from functools import wraps
def wrapper(method):
#wraps(method)
def _impl(self, *method_args, **method_kwargs):
method_output = method(self, *method_args, **method_kwargs)
return method_output + "!"
return _impl
class Foo:
#wrapper
def bar(self, word):
return word
f = Foo()
result = f.bar("kitty")
print(result)
Which will print:
kitty!
from re import search
from functools import wraps
def is_match(_lambda, pattern):
def wrapper(f):
#wraps(f)
def wrapped(self, *f_args, **f_kwargs):
if callable(_lambda) and search(pattern, (_lambda(self) or '')):
f(self, *f_args, **f_kwargs)
return wrapped
return wrapper
class MyTest(object):
def __init__(self):
self.name = 'foo'
self.surname = 'bar'
#is_match(lambda x: x.name, 'foo')
#is_match(lambda x: x.surname, 'foo')
def my_rule(self):
print 'my_rule : ok'
#is_match(lambda x: x.name, 'foo')
#is_match(lambda x: x.surname, 'bar')
def my_rule2(self):
print 'my_rule2 : ok'
test = MyTest()
test.my_rule()
test.my_rule2()
ouput:
my_rule2 : ok
Another option would be to abandon the syntactic sugar and decorate in the __init__ of the class.
def countdown(number):
def countdown_decorator(func):
def func_wrapper():
for index in reversed(range(1, number+1)):
print(index)
func()
return func_wrapper
return countdown_decorator
class MySuperClass():
def __init__(self, number):
self.number = number
self.do_thing = countdown(number)(self.do_thing)
def do_thing(self):
print('im doing stuff!')
myclass = MySuperClass(3)
myclass.do_thing()
which would print
3
2
1
im doing stuff!
I know this issue is quite old, but the below workaround hasn't been proposed before. The problem here is that you can't access self in a class block, but you can in a class method.
Let's create a dummy decorator to repeat a function some times.
import functools
def repeat(num_rep):
def decorator_repeat(func):
#functools.wraps(func)
def wrapper_repeat(*args, **kwargs):
for _ in range(num_rep):
value = func(*args, **kwargs)
return
return wrapper_repeat
return decorator_repeat
class A:
def __init__(self, times, name):
self.times = times
self.name = name
def get_name(self):
#repeat(num_rep=self.times)
def _get_name():
print(f'Hi {self.name}')
_get_name()
I know this is an old question, but this solution has not been mentioned yet, hopefully it may help someone even today, after 8 years.
So, what about wrapping a wrapper? Let's assume one cannot change the decorator neither decorate those methods in init (they may be #property decorated or whatever). There is always a possibility to create custom, class-specific decorator that will capture self and subsequently call the original decorator, passing runtime attribute to it.
Here is a working example (f-strings require python 3.6):
import functools
# imagine this is at some different place and cannot be changed
def check_authorization(some_attr, url):
def decorator(func):
#functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"checking authorization for '{url}'...")
return func(*args, **kwargs)
return wrapper
return decorator
# another dummy function to make the example work
def do_work():
print("work is done...")
###################
# wrapped wrapper #
###################
def custom_check_authorization(some_attr):
def decorator(func):
# assuming this will be used only on this particular class
#functools.wraps(func)
def wrapper(self, *args, **kwargs):
# get url
url = self.url
# decorate function with original decorator, pass url
return check_authorization(some_attr, url)(func)(self, *args, **kwargs)
return wrapper
return decorator
#############################
# original example, updated #
#############################
class Client(object):
def __init__(self, url):
self.url = url
#custom_check_authorization("some_attr")
def get(self):
do_work()
# create object
client = Client(r"https://stackoverflow.com/questions/11731136/class-method-decorator-with-self-arguments")
# call decorated function
client.get()
output:
checking authorisation for 'https://stackoverflow.com/questions/11731136/class-method-decorator-with-self-arguments'...
work is done...
You can't. There's no self in the class body, because no instance exists. You'd need to pass it, say, a str containing the attribute name to lookup on the instance, which the returned function can then do, or use a different method entirely.
It will be very useful to have a general-purpose utility, that can turn any decorator for functions, into decorator for methods. I thought about it for an hour, and actually come up with one:
from typing import Callable
Decorator = Callable[[Callable], Callable]
def decorate_method(dec_for_function: Decorator) -> Decorator:
def dec_for_method(unbounded_method) -> Callable:
# here, `unbounded_method` will be a unbounded function, whose
# invokation must have its first arg as a valid `self`. When it
# return, it also must return an unbounded method.
def decorated_unbounded_method(self, *args, **kwargs):
#dec_for_function
def bounded_method(*args, **kwargs):
return unbounded_method(self, *args, **kwargs)
return bounded_method(*args, **kwargs)
return decorated_unbounded_method
return dec_for_method
The usage is:
# for any decorator (with or without arguments)
#some_decorator_with_arguments(1, 2, 3)
def xyz(...): ...
# use it on a method:
class ABC:
#decorate_method(some_decorator_with_arguments(1, 2, 3))
def xyz(self, ...): ...
Test:
def dec_for_add(fn):
"""This decorator expects a function: (x,y) -> int.
If you use it on a method (self, x, y) -> int, it will fail at runtime.
"""
print(f"decorating: {fn}")
def add_fn(x,y):
print(f"Adding {x} + {y} by using {fn}")
return fn(x,y)
return add_fn
#dec_for_add
def add(x,y):
return x+y
add(1,2) # OK!
class A:
#dec_for_add
def f(self, x, y):
# ensure `self` is still a valid instance
assert isinstance(self, A)
return x+y
# TypeError: add_fn() takes 2 positional arguments but 3 were given
# A().f(1,2)
class A:
#decorate_method(dec_for_add)
def f(self, x, y):
# ensure `self` is still a valid instance
assert isinstance(self, A)
return x+y
# Now works!!
A().f(1,2)
This question already has answers here:
How to create a decorator that can be used either with or without parameters?
(16 answers)
Closed 3 years ago.
from functools import wraps
def foo_register(method_name=None):
"""Does stuff."""
def decorator(method):
if method_name is None:
method.gw_method = method.__name__
else:
method.gw_method = method_name
#wraps(method)
def wrapper(*args, **kwargs):
method(*args, **kwargs)
return wrapper
return decorator
Example: The following decorates my_function with foo_register instead of ever making it to decorator.
#foo_register
def my_function():
print('hi...')
Example: The following works as expected.
#foo_register('say_hi')
def my_function():
print('hi...')
If I want it to work correctly in both applications (one using method.__name__ and one passing the name in), I have to check inside of foo_register to see if the first argument is a decorator, and if so, I have to: return decorator(method_name) (instead of return decorator). This sort of "check to see if it's a callable" seems very hackish. Is there a nicer way to create a multi-use decorator like this?
P.S. I already know that I can require the decorator to be called, but that's not a "solution". I want the API to feel natural. My wife loves decorating, and I don't want to ruin that.
The cleanest way I know of for doing this is the following:
import functools
def decorator(original_function=None, optional_argument1=None, optional_argument2=None, ...):
def _decorate(function):
#functools.wraps(function)
def wrapped_function(*args, **kwargs):
...
return wrapped_function
if original_function:
return _decorate(original_function)
return _decorate
Explanation
When the decorator is called with no optional arguments like this:
#decorator
def function ...
The function is passed as the first argument and decorate returns the decorated function, as expected.
If the decorator is called with one or more optional arguments like this:
#decorator(optional_argument1='some value')
def function ....
Then decorator is called with the function argument with value None, so a function that decorates is returned, as expected.
Python 3
Note that the decorator signature above may be improved with Python 3-specific *, syntax to enforce safe use of keyword arguments. Simply replace the signature of the outermost function with:
def decorator(original_function=None, *, optional_argument1=None, optional_argument2=None, ...):
Through the help of the answers here and elsewhere and a bunch of trial and error I've found that there is actually a far easier and generic way to make decorators take optional arguments. It does check the args it was called with but there isn't any other way to do it.
The key is to decorate your decorator.
Generic decorator decorator code
Here is the decorator decorator (this code is generic and can be used by anyone who needs an optional arg decorator):
def optional_arg_decorator(fn):
def wrapped_decorator(*args):
if len(args) == 1 and callable(args[0]):
return fn(args[0])
else:
def real_decorator(decoratee):
return fn(decoratee, *args)
return real_decorator
return wrapped_decorator
Usage
Using it is as easy as:
Create a decorator like normal.
After the first target function argument, add your optional arguments.
Decorate the decorator with optional_arg_decorator
Example:
#optional_arg_decorator
def example_decorator_with_args(fn, optional_arg = 'Default Value'):
...
return fn
Test cases
For your use case:
So for your case, to save an attribute on the function with the passed-in method name or the __name__ if None:
#optional_arg_decorator
def register_method(fn, method_name = None):
fn.gw_method = method_name or fn.__name__
return fn
Add decorated methods
Now you have a decorator that is usable with or without args:
#register_method('Custom Name')
def custom_name():
pass
#register_method
def default_name():
pass
assert custom_name.gw_method == 'Custom Name'
assert default_name.gw_method == 'default_name'
print 'Test passes :)'
Glenn - I had to do it then. I guess I'm glad that there is not a "magic" way to do it. I hate those.
So, here's my own answer (method names different than above, but same concept):
from functools import wraps
def register_gw_method(method_or_name):
"""Cool!"""
def decorator(method):
if callable(method_or_name):
method.gw_method = method.__name__
else:
method.gw_method = method_or_name
#wraps(method)
def wrapper(*args, **kwargs):
method(*args, **kwargs)
return wrapper
if callable(method_or_name):
return decorator(method_or_name)
return decorator
Example usage (both versions work the same):
#register_gw_method
def my_function():
print('hi...')
#register_gw_method('say_hi')
def my_function():
print('hi...')
How about
from functools import wraps, partial
def foo_register(method=None, string=None):
if not callable(method):
return partial(foo_register, string=method)
method.gw_method = string or method.__name__
#wraps(method)
def wrapper(*args, **kwargs):
method(*args, **kwargs)
return wrapper
Enhanced Generic Decorator Decorator Code
Here's my adaption of #Nicole's answer with the following enhancements:
optional kwargs may be passed to the decorated decorator
the decorated decorator may be a bound method
import functools
def optional_arg_decorator(fn):
#functools.wraps(fn)
def wrapped_decorator(*args, **kwargs):
is_bound_method = hasattr(args[0], fn.__name__) if args else False
if is_bound_method:
klass = args[0]
args = args[1:]
# If no arguments were passed...
if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
if is_bound_method:
return fn(klass, args[0])
else:
return fn(args[0])
else:
def real_decorator(decoratee):
if is_bound_method:
return fn(klass, decoratee, *args, **kwargs)
else:
return fn(decoratee, *args, **kwargs)
return real_decorator
return wrapped_decorator
Now that this old thread is back at the top anyway, lemme just throw in some Decorator-ception:
def magical_decorator(decorator):
#wraps(decorator)
def inner(*args, **kw):
if len(args) == 1 and not kw and callable(args[0]):
return decorator()(args[0])
else:
return decorator(*args, **kw)
return inner
Now your magical decorator is just a single line away!
#magical_decorator
def foo_register(...):
# bla bla
By the way, this works for any decorator. It just causes #foo to behave (as close as possibly) like #foo().
A generic decorator for decorating decorator definitions, expressing that decorated decorator accepts default arguments, which are set if none are explicitly given.
from functools import wraps
def default_arguments(*default_args, **default_kwargs):
def _dwrapper(decorator):
#wraps(decorator)
def _fwrapper(*args, **kwargs):
if callable(args[0]) and len(args) == 1 and not kwargs:
return decorator(*default_args, **default_kwargs)(args[0])
return decorator(*args, **kwargs)
return _fwrapper
return _dwrapper
It can be used in either of ways.
from functools import lru_cache # memoization decorator from Python 3
# apply decorator to decorator post definition
lru_cache = (default_arguments(maxsize=100)) (lru_cache)
# could also be:
# #default_arguments(maxsize=100)
# class lru_cache(object):
# def __init__(self, maxsize):
# ...
# def __call__(self, wrapped_function):
# ...
#lru_cache # this works
def fibonacci(n):
...
#lru_cache(200) # this also works
def fibonacci(n):
...
If you want this functionality on multiple decorators you can evade the code boilerplate with a decorator for a decorator:
from functools import wraps
import inspect
def decorator_defaults(**defined_defaults):
def decorator(f):
args_names = inspect.getargspec(f)[0]
def wrapper(*new_args, **new_kwargs):
defaults = dict(defined_defaults, **new_kwargs)
if len(new_args) == 0:
return f(**defaults)
elif len(new_args) == 1 and callable(new_args[0]):
return f(**defaults)(new_args[0])
else:
too_many_args = False
if len(new_args) > len(args_names):
too_many_args = True
else:
for i in range(len(new_args)):
arg = new_args[i]
arg_name = args_names[i]
defaults[arg_name] = arg
if len(defaults) > len(args_names):
too_many_args = True
if not too_many_args:
final_defaults = []
for name in args_names:
final_defaults.append(defaults[name])
return f(*final_defaults)
if too_many_args:
raise TypeError("{0}() takes {1} argument(s) "
"but {2} were given".
format(f.__name__,
len(args_names),
len(defaults)))
return wrapper
return decorator
#decorator_defaults(start_val="-=[", end_val="]=-")
def my_text_decorator(start_val, end_val):
def decorator(f):
#wraps(f)
def wrapper(*args, **kwargs):
return "".join([f.__name__, ' ', start_val,
f(*args, **kwargs), end_val])
return wrapper
return decorator
#decorator_defaults(end_val="]=-")
def my_text_decorator2(start_val, end_val):
def decorator(f):
#wraps(f)
def wrapper(*args, **kwargs):
return "".join([f.__name__, ' ', start_val,
f(*args, **kwargs), end_val])
return wrapper
return decorator
#my_text_decorator
def func1a(value):
return value
#my_text_decorator()
def func2a(value):
return value
#my_text_decorator2("-=[")
def func2b(value):
return value
#my_text_decorator(end_val=" ...")
def func3a(value):
return value
#my_text_decorator2("-=[", end_val=" ...")
def func3b(value):
return value
#my_text_decorator("|> ", " <|")
def func4a(value):
return value
#my_text_decorator2("|> ", " <|")
def func4b(value):
return value
#my_text_decorator(end_val=" ...", start_val="|> ")
def func5a(value):
return value
#my_text_decorator2("|> ", end_val=" ...")
def func5b(value):
return value
print(func1a('My sample text')) # func1a -=[My sample text]=-
print(func2a('My sample text')) # func2a -=[My sample text]=-
print(func2b('My sample text')) # func2b -=[My sample text]=-
print(func3a('My sample text')) # func3a -=[My sample text ...
print(func3b('My sample text')) # func3b -=[My sample text ...
print(func4a('My sample text')) # func4a |> My sample text <|
print(func4b('My sample text')) # func4b |> My sample text <|
print(func5a('My sample text')) # func5a |> My sample text ...
print(func5b('My sample text')) # func5b |> My sample text ...
Note: it has the drawback where you can't pass 1 argument as function to decorator.
Note2: if you have tips/notes on how to improve this decorator, you could comment at code review: https://codereview.stackexchange.com/questions/78829/python-decorator-for-optional-arguments-decorator
I was incredibly annoyed by this issue and eventually wrote a library to solve it: decopatch.
It supports two development styles: nested (like in python decorator factories) and flat (one less level of nesting). This is how your example would be implemented in flat mode:
from decopatch import function_decorator, DECORATED
from makefun import wraps
#function_decorator
def foo_register(method_name=None, method=DECORATED):
if method_name is None:
method.gw_method = method.__name__
else:
method.gw_method = method_name
# create a signature-preserving wrapper
#wraps(method)
def wrapper(*args, **kwargs):
method(*args, **kwargs)
return wrapper
Note that I use makefun.wraps instead of functools.wraps here so that the signature is fully preserved (the wrapper is not called at all if the arguments are invalid).
decopatch supports an additional development style, that I call double-flat, that is dedicated to creating signature-preserving function wrappers like this one. Your example would be implemented like this:
from decopatch import function_decorator, WRAPPED, F_ARGS, F_KWARGS
#function_decorator
def foo_register(method_name=None,
method=WRAPPED, f_args=F_ARGS, f_kwargs=F_KWARGS):
# this is directly the wrapper
if method_name is None:
method.gw_method = method.__name__
else:
method.gw_method = method_name
method(*f_args, **f_kwargs)
Note that in this style, all of your code is executed in calls to method. This might not be desirable - you might wish to perform things once at decoration time only - for this the previous style would be better.
You can check that both styles work:
#foo_register
def my_function():
print('hi...')
#foo_register('say_hi')
def my_function():
print('hi...')
Please check the documentation for details.
Here's another variation, which is fairly concise and doesn't use functools:
def decorator(*args, **kwargs):
def inner_decorator(fn, foo=23, bar=42, abc=None):
'''Always passed <fn>, the function to decorate.
# Do whatever decorating is required.
...
if len(args)==1 and len(kwargs)==0 and callable(args[0]):
return inner_decorator(args[0])
else:
return lambda fn: inner_decorator(fn, *args, **kwargs)
Depending on whether inner_decorator can be called with only one parameter, one can then do #decorator, #decorator(), #decorator(24) etc.
This can be generalised to a 'decorator decorator':
def make_inner_decorator(inner_decorator):
def decorator(*args, **kwargs):
if len(args)==1 and len(kwargs)==0 and callable(args[0]):
return inner_decorator(args[0])
else:
return lambda fn: inner_decorator(fn, *args, **kwargs)
return decorator
#make_inner_decorator
def my_decorator(fn, a=34, b='foo'):
...
#my_decorator
def foo(): ...
#my_decorator()
def foo(): ...
#my_decorator(42)
def foo(): ...
Here is an other solution that work also if the optional argument is a callable:
def test_equal(func=None, optional_value=None):
if func is not None and optional_value is not None:
# prevent user to set func parameter manually
raise ValueError("Don't set 'func' parameter manually")
if optional_value is None:
optional_value = 10 # The default value (if needed)
def inner(function):
def func_wrapper(*args, **kwargs):
# do something
return function(*args, **kwargs) == optional_value
return func_wrapper
if not func:
return inner
return inner(func)
This way both syntax will work:
#test_equal
def does_return_10():
return 10
#test_equal(optional_value=20)
def does_return_20():
return 20
# does_return_10() return True
# does_return_20() return True
Here's my solution, written for python3. It has a different approach from the other ones since it defines a callable class rather than a function.
class flexible_decorator:
def __init__(self, arg="This is default"):
self.arg = arg
def __call__(self, func):
def wrapper(*args, **kwargs):
print("Calling decorated function. arg '%s'" % self.arg)
func(*args, **kwargs)
return wrapper
You still have to explicitly call the decorator
#flexible_decorator()
def f(foo):
print(foo)
#flexible_decorator(arg="This is not default")
def g(bar):
print(bar)
A similar solution like those checking the type and length of the arguments using callable classes
class decor(object):
def __init__(self, *args, **kwargs):
self.decor_args = args
self.decor_kwargs = kwargs
def __call__(self, *call_args, **call_kwargs):
if callable(self.decor_args[0]) and len(self.decor_args) == 1:
func = self.decor_args[0]
return self.__non_param__call__(func, call_args, call_kwargs)
else:
func = call_args[0]
return self.__param__call__(func)
def __non_param__call__(self, func, call_args, call_kwargs):
print "No args"
return func(*call_args, **call_kwargs)
def __param__call__(self, func):
def wrapper(*args, **kwargs):
print "With Args"
return func(*args, **kwargs)
return wrapper
#decor(a)
def test1(a):
print 'test' + a
#decor
def test2(b):
print 'test' + b
I've made a simple package to solve the problem
Installation
Master branch
pip install git+https://github.com/ferrine/biwrap
Latest release
pip install biwrap
Overview
Some wrappers may have optional arguments and we often want to avoid #wrapper() calls and use #wrapper instead.
This works for simple wrapper
import biwrap
#biwrap.biwrap
def hiwrap(fn, hi=True):
def new(*args, **kwargs):
if hi:
print('hi')
else:
print('bye')
return fn(*args, **kwargs)
return new
Defined wrapper can be used in both ways
#hiwrap
def fn(n):
print(n)
fn(1)
#> hi
#> 1
#hiwrap(hi=False)
def fn(n):
print(n)
fn(1)
#> bye
#> 1
biwrap also works for bound methods
class O:
#hiwrap(hi=False)
def fn(self, n):
print(n)
O().fn(1)
#> bye
#> 1
Class methods / properties are supported too
class O:
def __init__(self, n):
self.n = n
#classmethod
#hiwrap
def fn(cls, n):
print(n)
#property
#hiwrap(hi=False)
def num(self):
return self.n
o = O(2)
o.fn(1)
#> hi
#> 1
print(o.num)
#> bye
#> 2
Function like call is OK too
def fn(n):
print(n)
fn = hiwrap(fn, hi=False)
fn(1)
#> bye
#> 1
Can one write something like:
class Test(object):
def _decorator(self, foo):
foo()
#self._decorator
def bar(self):
pass
This fails: self in #self is unknown
I also tried:
#Test._decorator(self)
which also fails: Test unknown
I would like to temporarily change some instance variables
in the decorator and then run the decorated method, before
changing them back.
Would something like this do what you need?
class Test(object):
def _decorator(foo):
def magic( self ) :
print "start magic"
foo( self )
print "end magic"
return magic
#_decorator
def bar( self ) :
print "normal call"
test = Test()
test.bar()
This avoids the call to self to access the decorator and leaves it hidden in the class namespace as a regular method.
>>> import stackoverflow
>>> test = stackoverflow.Test()
>>> test.bar()
start magic
normal call
end magic
>>>
edited to answer question in comments:
How to use the hidden decorator in another class
class Test(object):
def _decorator(foo):
def magic( self ) :
print "start magic"
foo( self )
print "end magic"
return magic
#_decorator
def bar( self ) :
print "normal call"
_decorator = staticmethod( _decorator )
class TestB( Test ):
#Test._decorator
def bar( self ):
print "override bar in"
super( TestB, self ).bar()
print "override bar out"
print "Normal:"
test = Test()
test.bar()
print
print "Inherited:"
b = TestB()
b.bar()
print
Output:
Normal:
start magic
normal call
end magic
Inherited:
start magic
override bar in
start magic
normal call
end magic
override bar out
end magic
What you're wanting to do isn't possible. Take, for instance, whether or not the code below looks valid:
class Test(object):
def _decorator(self, foo):
foo()
def bar(self):
pass
bar = self._decorator(bar)
It, of course, isn't valid since self isn't defined at that point. The same goes for Test as it won't be defined until the class itself is defined (which its in the process of). I'm showing you this code snippet because this is what your decorator snippet transforms into.
So, as you can see, accessing the instance in a decorator like that isn't really possible since decorators are applied during the definition of whatever function/method they are attached to and not during instantiation.
If you need class-level access, try this:
class Test(object):
#classmethod
def _decorator(cls, foo):
foo()
def bar(self):
pass
Test.bar = Test._decorator(Test.bar)
import functools
class Example:
def wrapper(func):
#functools.wraps(func)
def wrap(self, *args, **kwargs):
print("inside wrap")
return func(self, *args, **kwargs)
return wrap
#wrapper
def method(self):
print("METHOD")
wrapper = staticmethod(wrapper)
e = Example()
e.method()
This is one way to access(and have used) self from inside a decorator defined inside the same class:
class Thing(object):
def __init__(self, name):
self.name = name
def debug_name(function):
def debug_wrapper(*args):
self = args[0]
print 'self.name = ' + self.name
print 'running function {}()'.format(function.__name__)
function(*args)
print 'self.name = ' + self.name
return debug_wrapper
#debug_name
def set_name(self, new_name):
self.name = new_name
Output (tested on Python 2.7.10):
>>> a = Thing('A')
>>> a.name
'A'
>>> a.set_name('B')
self.name = A
running function set_name()
self.name = B
>>> a.name
'B'
The example above is silly, but it works.
Here's an expansion on Michael Speer's answer to take it a few steps further:
An instance method decorator which takes arguments and acts on a function with arguments and a return value.
class Test(object):
"Prints if x == y. Throws an error otherwise."
def __init__(self, x):
self.x = x
def _outer_decorator(y):
def _decorator(foo):
def magic(self, *args, **kwargs) :
print("start magic")
if self.x == y:
return foo(self, *args, **kwargs)
else:
raise ValueError("x ({}) != y ({})".format(self.x, y))
print("end magic")
return magic
return _decorator
#_outer_decorator(y=3)
def bar(self, *args, **kwargs) :
print("normal call")
print("args: {}".format(args))
print("kwargs: {}".format(kwargs))
return 27
And then
In [2]:
test = Test(3)
test.bar(
13,
'Test',
q=9,
lollipop=[1,2,3]
)
start magic
normal call
args: (13, 'Test')
kwargs: {'q': 9, 'lollipop': [1, 2, 3]}
Out[2]:
27
In [3]:
test = Test(4)
test.bar(
13,
'Test',
q=9,
lollipop=[1,2,3]
)
start magic
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-3-576146b3d37e> in <module>()
4 'Test',
5 q=9,
----> 6 lollipop=[1,2,3]
7 )
<ipython-input-1-428f22ac6c9b> in magic(self, *args, **kwargs)
11 return foo(self, *args, **kwargs)
12 else:
---> 13 raise ValueError("x ({}) != y ({})".format(self.x, y))
14 print("end magic")
15 return magic
ValueError: x (4) != y (3)
I found this question while researching a very similar problem. My solution is to split the problem into two parts. First, you need to capture the data that you want to associate with the class methods. In this case, handler_for will associate a Unix command with handler for that command's output.
class OutputAnalysis(object):
"analyze the output of diagnostic commands"
def handler_for(name):
"decorator to associate a function with a command"
def wrapper(func):
func.handler_for = name
return func
return wrapper
# associate mount_p with 'mount_-p.txt'
#handler_for('mount -p')
def mount_p(self, slurped):
pass
Now that we've associated some data with each class method, we need to gather that data and store it in a class attribute.
OutputAnalysis.cmd_handler = {}
for value in OutputAnalysis.__dict__.itervalues():
try:
OutputAnalysis.cmd_handler[value.handler_for] = value
except AttributeError:
pass
I use this type of decorator in some debugging situations, it allows overriding class properties by decorating, without having to find the calling function.
class myclass(object):
def __init__(self):
self.property = "HELLO"
#adecorator(property="GOODBYE")
def method(self):
print self.property
Here is the decorator code
class adecorator (object):
def __init__ (self, *args, **kwargs):
# store arguments passed to the decorator
self.args = args
self.kwargs = kwargs
def __call__(self, func):
def newf(*args, **kwargs):
#the 'self' for a method function is passed as args[0]
slf = args[0]
# replace and store the attributes
saved = {}
for k,v in self.kwargs.items():
if hasattr(slf, k):
saved[k] = getattr(slf,k)
setattr(slf, k, v)
# call the method
ret = func(*args, **kwargs)
#put things back
for k,v in saved.items():
setattr(slf, k, v)
return ret
newf.__doc__ = func.__doc__
return newf
Note: because I've used a class decorator you'll need to use #adecorator() with the brackets on to decorate functions, even if you don't pass any arguments to the decorator class constructor.
The simple way to do it.
All you need is to put the decorator method outside the class.
You can still use it inside.
def my_decorator(func):
#this is the key line. There's the aditional self parameter
def wrap(self, *args, **kwargs):
# you can use self here as if you were inside the class
return func(self, *args, **kwargs)
return wrap
class Test(object):
#my_decorator
def bar(self):
pass
Declare in inner class.
This solution is pretty solid and recommended.
class Test(object):
class Decorators(object):
#staticmethod
def decorator(foo):
def magic(self, *args, **kwargs) :
print("start magic")
foo(self, *args, **kwargs)
print("end magic")
return magic
#Decorators.decorator
def bar( self ) :
print("normal call")
test = Test()
test.bar()
The result:
>>> test = Test()
>>> test.bar()
start magic
normal call
end magic
>>>
Decorators seem better suited to modify the functionality of an entire object (including function objects) versus the functionality of an object method which in general will depend on instance attributes. For example:
def mod_bar(cls):
# returns modified class
def decorate(fcn):
# returns decorated function
def new_fcn(self):
print self.start_str
print fcn(self)
print self.end_str
return new_fcn
cls.bar = decorate(cls.bar)
return cls
#mod_bar
class Test(object):
def __init__(self):
self.start_str = "starting dec"
self.end_str = "ending dec"
def bar(self):
return "bar"
The output is:
>>> import Test
>>> a = Test()
>>> a.bar()
starting dec
bar
ending dec
I have a Implementation of Decorators that Might Help
import functools
import datetime
class Decorator(object):
def __init__(self):
pass
def execution_time(func):
#functools.wraps(func)
def wrap(self, *args, **kwargs):
""" Wrapper Function """
start = datetime.datetime.now()
Tem = func(self, *args, **kwargs)
end = datetime.datetime.now()
print("Exection Time:{}".format(end-start))
return Tem
return wrap
class Test(Decorator):
def __init__(self):
self._MethodName = Test.funca.__name__
#Decorator.execution_time
def funca(self):
print("Running Function : {}".format(self._MethodName))
return True
if __name__ == "__main__":
obj = Test()
data = obj.funca()
print(data)
You can decorate the decorator:
import decorator
class Test(object):
#decorator.decorator
def _decorator(foo, self):
foo(self)
#_decorator
def bar(self):
pass