Using decorators with argument in Python

Ben Finney ben+python at benfinney.id.au
Tue Jun 28 18:19:34 EDT 2011


Jigar Tanna <poisonousrattle5 at gmail.com> writes:

> where I came across a special case of using arguments with decorators

A decorator is a function which takes exactly one parameter, and returns
a function based on that parameter.

    <URL:http://docs.python.org/glossary.html#term-decorator>
    <URL:http://docs.python.org/reference/compound_stmts.html#function>

So a decorator always takes an argument: the function to be decorated.

> Below is the code for memoization where I was looking at the
> concept...

[…]

> def memoize(get_key=get_key, cache=cache) :
>     def _memoize( function) :
>         print function
>         def __memoize(*args, **kw) :
>             key = get_key(function, *args, **kw)
>             try:
>                 return cache[key]
>             except KeyError:
>                 cache[key] = function( *args, **kw)
>                 return cache[key]
>         return __memoize
>     return _memoize

Note that the function ‘memoize’ is not a decorator. It's a decorator
creator; it returns a decorator.

That is, you pass this function zero, one, or two arguments, and it
defines a new function with the name ‘_memoize’; *that* function is then
returned. That function is the decorator, and will receive exactly one
parameter when you decorate a function.

> @memoize()

‘memoize()’ calls the ‘memoize’ function, which creates and returns the
decorator. That return value is then used (because of the ‘@’ syntax) to
decorate the function whose definition follows.

> def factory(n) :
>     return n * n

This function is created, and then immediately passed as the single
argument to the decorator that ‘memoize()’ returned above. The return
value from that decorator then gets bound to the name ‘factory’.

(Side note: it's unfortunate the writer of ‘memoize’ didn't wrap the
decorated function better. In this implementation, the resulting
function will be bound to the name ‘factory’, but will consider its name
to be ‘_memoize’. It will also lack the docstring of the ‘factory’
function. For a better way, see ‘functools.wraps’.)

> coming across to certain views from people, it is not a good practice
> to use decorators with arguments (i.e. @memoize() )

Correcting your mental model: those are not decorators. They are
creating a decorator, by calling a function that returns a decorator.

And if you've come across views that say there's something wrong with
that, those views are mistaken. There are many good reasons to call a
function to return a decorator, and “I get a different decorator
depending on what arguments I give to this decorator-creator” is the
pattern.

> and instead it is good to just use @memoize.

That wouldn't work in this case, of course, since the function ‘memoize’
is not a decorator: it doesn't take the function-to-be-decorated as an
argument.

In other cases where the decorator is available immediately as a defined
function (e.g. ‘functools.partial’ or the built-in ‘property’), there is
of course no problem using that.

But it's not always the case that you have that decorator yet, and
calling another function (with whatever arguments it needs) to create
the decorator at the point where you need to use it is also fine.

> Can any of you guys explain me advantages and disadvantages of using
> each of them

I hope that explains.

-- 
 \     “Men never do evil so completely and cheerfully as when they do |
  `\        it from religious conviction.” —Blaise Pascal (1623–1662), |
_o__)                                                   Pensées, #894. |
Ben Finney



More information about the Python-list mailing list