.. _decorating-for-smart-people:

==============================
 What is a decorator, really?
==============================

(with thanks to Fernando Perez for setting me straight on this stuff - see https://mail.scipy.org/pipermail/ipython-dev/2009-September/005500.html)

You may want to have a look at the Python docs:

* https://docs.python.org/3/glossary.html#term-decorator
* https://docs.python.org/3/reference/compound_stmts.html#function

The anatomy of the decorator thing is this::

   @decorator-expression
   function-definition

``decorator-expression`` is an *expression* that returns a callable
thing. The callable thing has to take single argument. That's all it is.
``function-definition`` is a function definition (it therefore must
begin with ``def``).  If ``decorator-expression`` is ``dec_func`` and
``function-definition`` is ``def func(): pass`` then the syntax above is
exactly the result of::

   def func(): pass
   func = dec_func(func)

Decorator expression
====================

The decorator expression is an expression that returns a callable thing.

For example, it could be a reference to a callable thing:

>>> def afunc(f): pass
>>> @afunc
... def bfunc(): pass

or an *expression that returns a reference* to a callable thing:

>>> def cfunc(value):
...    def anon_func(f):
...        return value
...    return anon_func
>>> @cfunc('a value')
... def bfunc(): pass

The callable thing can be anything that is callable - for example, a class:

>>> class C(object):
...     def __call__(self, value):
...         pass
>>> @C()
... def bfunc(): pass

The callable thing has to take one argument:

>>> def dfunc(): pass
>>> @dfunc
... def bfunc(): pass
Traceback (most recent call last):
  ...
TypeError: dfunc() takes no arguments (1 given)

and, the callable thing has to be callable:

>>> not_a_func = 'a string'
>>> @not_a_func
... def bfunc(): pass
Traceback (most recent call last):
   ...
TypeError: 'str' object is not callable

The decorator expression result
===============================

The decorator expression result is the result of evaluating the
decorator expression.

For example, in this case:

>>> def cfunc(value):
...    def anon_func(f):
...        return value
...    return anon_func
>>> @cfunc('a value')
... def bfunc(): pass

the decorator expression result is equivalent to:

>>> def anon_func(f):
...     return 'a value'
>>> dec_exp_res = anon_func

Note that, the decorator expression result will process the
``function-definition`` like this:

>>> def func(): pass # function definition
>>> func = dec_exp_res(func)

That is what the syntax says.  Usually, as in
:ref:`decorating-for-dummies`, you will want to return a modified
function.  But, given the syntax, you can do anything you want:

>>> def strange_decorator(f):
...     print 'I am strange'
>>> @strange_decorator
... def bfunc(): pass
I am strange

This fits the syntax (the decorator expression resolves to a callable
thing that takes one argument) but, this time, the decorator just prints
something.  This may seem a little bizarre, but - there are uses...


