Using **kwargs
in function definition, you get keyword arguments (not positional arguments) as a dictionary:
>>> def lt_dec(func):
... def _lt_lower(**kwargs):
... print 'type(kwargs)=', type(kwargs)
... print 'kwargs=', kwargs
... kwargs['lane_type'] = kwargs['lane_type'].lower()
... return func(**kwargs)
... return _lt_lower
...
>>> @lt_dec
... def test1(lane_type):
... print lane_type
...
>>> test1(lane_type='Solid')
type(kwargs)= <type 'dict'>
kwargs= {'lane_type': 'Solid'}
solid
>>> def lt_dec(func):
...def _lt_lower( * args, ** kwargs):
...
if args:
...args = (args[0].lower(), ) + args[1: ]
...elif 'lane_type' in kwargs:
...kwargs['lane_type'] = kwargs['lane_type'].lower()
...
return func( * args, ** kwargs)
...
return _lt_lower
...
>>>
@lt_dec
...def test1(lane_type):
...print lane_type
...
>>>
test1('Solid')
solid
>>>
test1(lane_type = 'Solid')
solid
Using inspect.getcallargs
:
>>>
import inspect
>>>
>>>
def lt_dec(func):
...def _lt_lower( * args, ** kwargs):
...kwargs = inspect.getcallargs(func, * args, ** kwargs)
...
if 'lane_type' in kwargs:
...kwargs['lane_type'] = kwargs['lane_type'].lower()
...
return func( ** kwargs)
...
return _lt_lower
...
>>>
@lt_dec
...def test1(blah, lane_type):
...print lane_type
...
>>>
test1('foo', 'Solid')
solid
>>>
test1('foo', lane_type = 'Solid')
solid
This is the version of decorator that reduces impact of inspect by using it at decoration time - and not at the execution time. The execution impact is still relatively high - but 100 times lower than in the original decorator you proposed (just FYI)
def lane_type_lower(func):
def _conv_pos( * args, ** kwargs):
args = list(args);
args[pos] = args[pos].lower()
return args, kwargs
def _conv_kw( * args, ** kwargs):
kwargs['lane_type'] = kwargs['lane_type'].lower()
return args, kwargs
insp = inspect.getargspec(func)
try:
pos = insp.args.index('lane_type')
conv_func = _conv_pos
except ValueError:
conv_func = _conv_kw
def _lane_type_lower( * args, ** kwargs):
args, kwargs = conv_func( * args, ** kwargs)
return func( * args, ** kwargs)
return _lane_type_lower
I have a set of functions/methods that all have different set of positional arguments and- in some cases - keyword arguments too, but share one string argument named lane_type. It may be either positional or keyword argument. Due to design flaw (guilty as charged) the same value in different places may have capital letters in the string. Therefore for comparisons I have to convert it to lower case. Eventually I decided to try and make the conversion through the decorator:,Using **kwargs in function definition, you get keyword arguments (not positional arguments) as a dictionary:, The lists of positional and keyword arguments is joined together to one signature string with each argument separated by a comma. The return value is printed after the function is executed. Let’s see how the decorator works in practice by applying it to a simple function with one position and one keyword argument: , A decorator takes just one argument: the function to be decorated. There is no way to pass other arguments. But additional arguments are often desired. The trick is then to make a function which takes arbitrary arguments and returns a decorator.
def lt_dec(func): def _lt_lower( ** kwargs): kwargs['lane_type'] = kwargs['lane_type'].lower() return func( ** kwargs) return _lt_lower
>>> def lt_dec(func): ... def _lt_lower(**kwargs): ... print 'type(kwargs)=', type(kwargs) ... print 'kwargs=', kwargs ... kwargs['lane_type'] = kwargs['lane_type'].lower() ... return func(**kwargs) ... return _lt_lower ... >>> @lt_dec ... def test1(lane_type): ... print lane_type ... >>> test1(lane_type='Solid') type(kwargs)= <type 'dict'> kwargs= {'lane_type': 'Solid'} solid
def lt_dec(func): def _lt_lower( ** kwargs): kwargs['lane_type'] = kwargs['lane_type'].lower() return func( ** kwargs) return _lt_lower
In [38]: @lt_dec def test1(lane_type):print lane_type ....:In [39]: test1('Solid') --------------------------------------------------------------------------- TypeError Traceback (most recent call last) /homes/markg/<ipython-input-39-bb6cef5c7fad>in <module>() ---->1 test1('Solid') TypeError: _lt_lower() takes exactly 0 arguments (1 given)
def add_polygon(self, points, lane_type, color):
def __init__(self, points, lane_type, color, side = None):
A decorator takes just one argument: the function to be decorated. There is no way to pass other arguments.,But additional arguments are often desired. The trick is then to make a function which takes arbitrary arguments and returns a decorator.,With such decorator factories you must call the decorator with a pair of parentheses:,TypeError: decorator() missing 1 required positional argument: 'func'
Decorator functions
def decoratorfactory(message):
def decorator(func):
def wrapped_func( * args, ** kwargs):
print('The decorator wants to tell you: {}'.format(message))
return func( * args, ** kwargs)
return wrapped_func
return decorator
@decoratorfactory('Hello World')
def test():
pass
test()
With such decorator factories you must call the decorator with a pair of parentheses:
@decoratorfactory # Without parentheses
def test():
pass
test()
Decorator classes
def decoratorfactory( * decorator_args, ** decorator_kwargs):
class Decorator(object):
def __init__(self, func):
self.func = func
def __call__(self, * args, ** kwargs):
print('Inside the decorator with arguments {}'.format(decorator_args))
return self.func( * args, ** kwargs)
return Decorator
@decoratorfactory(10)
def test():
pass
test()
A keen observer will notice that parameters of the nested inner() function inside the decorator is the same as the parameters of functions it decorates. Taking this into account, now we can make general decorators that work with any number of parameters.,The above decorator was simple and it only worked with functions that did not have any parameters. What if we had functions that took in parameters like:,In this manner, we can decorate functions that take parameters.,The function ordinary() got decorated and the returned function was given the name pretty.
Here is an example.
def first(msg):
print(msg)
first("Hello")
second = first
second("Hello")
Output
Hello Hello
Such functions that take other functions as arguments are also called higher order functions. Here is an example of such a function.
def inc(x):
return x + 1
def dec(x):
return x - 1
def operate(func, x):
result = func(x)
return result
Furthermore, a function can return another function.
def is_called():
def is_returned():
print("Hello")
return is_returned
new = is_called()
# Outputs "Hello"
new()
Basically, a decorator takes in a function, adds some functionality and returns it.
def make_pretty(func):
def inner():
print("I got decorated")
func()
return inner
def ordinary():
print("I am ordinary")