In Python, what is the difference between using the same decorator with and without parentheses?
For example:
Without parentheses:
#some_decorator
def some_method():
pass
With parentheses:
#some_decorator()
def some_method():
pass
some_decorator in the first code snippet is a regular decorator:
#some_decorator
def some_method():
pass
is equivalent to
some_method = some_decorator(some_method)
On the other hand, some_decorator in the second code snippet is a callable that returns a decorator:
#some_decorator()
def some_method():
pass
is equivalent to
some_method = some_decorator()(some_method)
As pointed out by Duncan in comments, some decorators are designed to work both ways. Here's a pretty basic implementation of such decorator:
def some_decorator(arg=None):
def decorator(func):
def wrapper(*a, **ka):
return func(*a, **ka)
return wrapper
if callable(arg):
return decorator(arg) # return 'wrapper'
else:
return decorator # ... or 'decorator'
pytest.fixture is a more complex example.
Briefly speaking, decorators allow adding rich features to groups of functions and classes without modifying them at all.
The key to understand the difference between #some_decorator and #some_decorator() is that the former is a decorator, while the latter is a function (or callable) that returns a decorator.
I believe that seeing an implementation of each case facilitates understanding the difference:
#some_decorator
def some_decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
Application:
#some_decorator
def some_method():
pass
Equivalence:
some_method = some_decorator(some_method)
#some_decorator()
def some_decorator():
def decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
return decorator
Application:
#some_decorator()
def some_method():
pass
Equivalence:
some_method = some_decorator()(some_method)
Notice that now it is easier to see that #some_decorator() is a function returning a decorator while some_decorator is just a decorator. Keep in mind that some decorators are written to work both ways.
So now you might be wondering why we have these two cases when the former version seems simpler. The answer is that if you want to pass arguments to a decorator, using #some_decorator() will allow you to do this. Let's see some code in action:
def some_decorator(arg1, arg2):
def decorator(func):
def wrapper(*args, **kwargs):
print(arg1)
print(arg2)
return func(*args, **kwargs)
return wrapper
return decorator
Application:
#some_decorator('hello', 'bye')
def some_method():
pass
Equivalence:
some_method = some_decorator('hello', 'bye')(some_method)
Note: I think that it is worth to mention that a decorator can be implemented as a function or as a class. Check this for more information.
If you have a decorator that can be used with or without parameters, you can use the following decorator on your decorator to make it usable with or without parentheses, like this:
>>> #omittable_parentheses(allow_partial=True)
... def multiplier(multiply_by=2):
... def decorator(func):
... def multiplying_wrapper(*args, **kwargs):
... return multiply_by * func(*args, **kwargs)
... return multiplying_wrapper
... return decorator
...
>>> #multiplier
... def no_parentheses():
... return 2
...
>>> no_parentheses()
4
>>> #multiplier()
... def parentheses():
... return 2
...
>>> parentheses()
4
>>> #multiplier(3)
... def parameter():
... return 2
...
>>> parameter()
6
If allow_partial=True is given, the resulting decorator will also work with with partial:
>>> from functools import partial
>>> multiply_by_3 = partial(multiplier, multiply_by=3)
>>>
>>> #multiply_by_3
... def partial_no_parentheses():
... return 2
...
>>> partial_no_parentheses()
6
>>> #multiply_by_3()
... def partial_parentheses():
... return 2
...
>>> partial_parentheses()
6
Decorator code:
from functools import wraps
def omittable_parentheses(maybe_decorator=None, /, allow_partial=False):
"""A decorator for decorators that allows them to be used without parentheses"""
def decorator(func):
#wraps(decorator)
def wrapper(*args, **kwargs):
if len(args) == 1 and callable(args[0]):
if allow_partial:
return func(**kwargs)(args[0])
elif not kwargs:
return func()(args[0])
return func(*args, **kwargs)
return wrapper
if maybe_decorator is None:
return decorator
else:
return decorator(maybe_decorator)
As a bonus, this decorator decorator can be itself used with or without parentheses!
Some actually working code where you use the arg inside decorator:
def someDecorator(arg=None):
def decorator(func):
def wrapper(*a, **ka):
if not callable(arg):
print (arg)
return func(*a, **ka)
else:
return 'xxxxx'
return wrapper
if callable(arg):
return decorator(arg) # return 'wrapper'
else:
return decorator # ... or 'decorator'
#someDecorator(arg=1)
def my_func():
print('aaa')
#someDecorator
def my_func1():
print('bbb')
if __name__ == "__main__":
my_func()
my_func1()
The output is:
1
aaa
Decorator functools.wraps calls method functools.update_wrapper.
I want to understand the need to use wrap instead of update_wrapper.
Why can't we use update_wrapper as decorator instead of using wraps?
For example:
from functools import update_wrapper
def wrap1(func):
def call_it(*args, **kwargs):
"""wrap func: call_it1"""
print('before call in call_it1')
return func(*args, **kwargs)
return update_wrapper(call_it, func)
#wrap1
def hello1():
"""test hello 1"""
print('hello world1')
hello1()
and
def wrap3(func):
#wraps(func)
def call_it(*args, **kwargs):
"""wrap func: call_it3"""
print('before call in call_it3')
return func(*args, **kwargs)
return call_it
#wrap3
def hello3():
"""test hello 3"""
print('hello world3')
hello3()
work. But why the following doesn't?
def wrap2(func):
#update_wrapper(wrapped=func) # error, see below
def call_it(*args, **kwargs):
"""wrap func: call_it2"""
print('before call in call_it2')
return func(*args, **kwargs)
return call_it
#wrap2
def hello2():
"""test hello 2"""
print('hello world2')
hello2()
with error
TypeError: update_wrapper() missing 1 required positional argument: 'wrapper'
The declarations of wraps and update_wrapper are:
#functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)
and
functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)
The positional argument wrapper is the first argument to update_wrapper,
so why doesn't the following pick up call_it as wrapper argument to update_wrapper?
#update_wrapper(wrapped=func)
def call_it(*args, **kwargs):
Is there some way to use update_wrapper as a decorator instead of using wraps?
Using # as a decorator basically boils down to this:
#(value)
def f(...):
...
Being the same as:
def f(...):
...
f = (value)(f)
Now if we apply it to your case:
#update_wrapper(wrapped=func)
def call_it(*args, **kwargs):
...
def call_it(*args, **kwargs):
...
call_it = update_wrapper(wrapped=func)(call_it)
The problem here is that the first time, it is only called with the second argument. It then raises the error immediately.
This is because update_wrapper is not designed to be used as a decorator, but wraps is a decorator (factory).
This might be pushing things a little too far, but mostly out of curiosity..
Would it be possible to have a callable object (function/class) that acts as both a Context Manager and a decorator at the same time:
def xxx(*args, **kw):
# or as a class
#xxx(foo, bar)
def im_decorated(a, b):
print('do the stuff')
with xxx(foo, bar):
print('do the stuff')
Starting in Python 3.2, support for this is even included in the standard library. Deriving from the class contextlib.ContextDecorator makes it easy to write classes that can be used as both, a decorator or a context manager. This functionality could be easily backported to Python 2.x -- here is a basic implementation:
class ContextDecorator(object):
def __call__(self, f):
#functools.wraps(f)
def decorated(*args, **kwds):
with self:
return f(*args, **kwds)
return decorated
Derive your context manager from this class and define the __enter__() and __exit__() methods as usual.
In Python 3.2+, you can define a context manager that is also a decorator using #contextlib.contextmanager.
From the docs:
contextmanager() uses ContextDecorator so the context managers it creates can be used as decorators as well as in with statements
Example usage:
>>> from contextlib import contextmanager
>>> #contextmanager
... def example_manager(message):
... print('Starting', message)
... try:
... yield
... finally:
... print('Done', message)
...
>>> with example_manager('printing Hello World'):
... print('Hello, World!')
...
Starting printing Hello World
Hello, World!
Done printing Hello World
>>>
>>> #example_manager('running my function')
... def some_function():
... print('Inside my function')
...
>>> some_function()
Starting running my function
Inside my function
Done running my function
class Decontext(object):
"""
makes a context manager also act as decorator
"""
def __init__(self, context_manager):
self._cm = context_manager
def __enter__(self):
return self._cm.__enter__()
def __exit__(self, *args, **kwds):
return self._cm.__exit__(*args, **kwds)
def __call__(self, func):
def wrapper(*args, **kwds):
with self:
return func(*args, **kwds)
return wrapper
now you can do:
mydeco = Decontext(some_context_manager)
and that allows both
#mydeco
def foo(...):
do_bar()
foo(...)
and
with mydeco:
do_bar()
Here's an example:
class ContextDecorator(object):
def __init__(self, foo, bar):
self.foo = foo
self.bar = bar
print("init", foo, bar)
def __call__(self, f):
print("call")
def wrapped_f():
print("about to call")
f()
print("done calling")
return wrapped_f
def __enter__(self):
print("enter")
def __exit__(self, exc_type, exc_val, exc_tb):
print("exit")
with ContextDecorator(1, 2):
print("with")
#ContextDecorator(3, 4)
def sample():
print("sample")
sample()
This prints:
init 1 2
enter
with
exit
init 3 4
call
about to call
sample
done calling
Although I agree (and upvoted) #jterrace here, I'm adding a very slight variation that returns the decorated function, and includes arguments for both the decorator and the decorated function.
class Decon:
def __init__(self, a=None, b=None, c=True):
self.a = a
self.b = b
self.c = c
def __enter__(self):
# only need to return self
# if you want access to it
# inside the context
return self
def __exit__(self, exit_type, exit_value, exit_traceback):
# clean up anything you need to
# otherwise, nothing much more here
pass
def __call__(self, func):
def decorator(*args, **kwargs):
with self:
return func(*args, **kwargs)
return decorator
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
from functools import wraps
def a():
a='aa'
def b():
b="bbb"
c=wraps(a)(b)
print c#what happen?
What is the wraps mean, example is the best.
Quoted from the documentation:
functools.wraps(wrapped[, assigned][, updated])
This is a convenience function for invoking partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated) as a function decorator when defining a wrapper function. For example:
>>> from functools import wraps
>>> def my_decorator(f):
... #wraps(f)
... def wrapper(*args, **kwds):
... print 'Calling decorated function'
... return f(*args, **kwds)
... return wrapper
...
>>> #my_decorator
... def example():
... """Docstring"""
... print 'Called example function'
...
>>> example()
Calling decorated function
Called example function
>>> example.__name__
'example'
>>> example.__doc__
'Docstring'
Without the use of this decorator factory, the name of the example function would have been 'wrapper', and the docstring of the original example() would have been lost.
def login_required(f):
#wraps(f)
def decorated_function(*args, **kwargs):
if session.get("user_id") is None:
return redirect("/login")
return f(*args, **kwargs)
return decorated_function
#app.route("/")
#login_required
def index():
return "TODO"
Here we can see that the function login_required becomes a new decorator. The behavior of enter in the router / is that every person must be login-in