decorator for functions with positional arguments, with commonly named argument

  • Last Update :
  • Techknowledgy :

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
>>>
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

Suggestion : 2

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):

Suggestion : 3

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()

Suggestion : 4

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")