What are Decorators?
In Python, functions are first-class objects. They can be passed as variables, and have attributes,
just like any other object. They can also be returned from other functions!
<span class="lineno">1</span> <span class="k">def</span> <span class="nf">outer_func</span><span class="p">():</span>
<span class="lineno">2</span> <span class="k">def</span> <span class="nf">inner_func</span><span class="p">():</span>
<span class="lineno">3</span> <span class="k">print</span><span class="p">(</span><span class="s">'Inner funk!'</span><span class="p">)</span>
<span class="lineno">4</span> <span class="k">return</span> <span class="n">inner_func</span> <span class="c"># notice, no parens!</span>
Note the absence of parentheses when returning inner_func
above. Here we are not calling the function,
but rather returning a reference to the function. We can use this reference to invoke inner_func
as follows:
>>> inner = outer_func()
>>> inner()
Inner funk!
>>>
The call to outer_func
above simply returns a reference to inner_func
, then we invoke inner_func
by
calling its reference as inner()
(with parentheses this time).
A decorator is simply a function that returns a function.
Creating a Decorator
Let’s start with the simplest decorator possible – one that does virtually nothing. We will call this construct the “Identity Decorator”:
<span class="lineno">1</span> <span class="k">def</span> <span class="nf">identity</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
<span class="lineno">2</span> <span class="k">def</span> <span class="nf">_identity</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="lineno">3</span> <span class="k">return</span> <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="lineno">4</span> <span class="k">return</span> <span class="n">_identity</span>
This decorator simply returns what ever function and argument(s) was passed to it, without modification. To use a decorator, prefix the method of your choice with the @
symbol followed by the decorator function name:
<span class="lineno">1</span> <span class="nd">@identity</span>
<span class="lineno">2</span> <span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="n">bar</span><span class="p">):</span>
<span class="lineno">3</span> <span class="k">pass</span>
The decorator identity
will take the function foo
as its parameter, and return it unmodified. Now let’s try something concrete.
Say Hello
We can create a decorator to print “Hello” to the console whenever the function it decorates is called.
<span class="lineno">1</span> <span class="k">def</span> <span class="nf">hello</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
<span class="lineno">2</span> <span class="k">def</span> <span class="nf">inner</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="lineno">3</span> <span class="k">print</span><span class="p">(</span><span class="s">"Hello"</span><span class="p">)</span>
<span class="lineno">4</span> <span class="k">return</span> <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="lineno">5</span> <span class="k">return</span> <span class="n">inner</span>
Here, the hello
function is the decorator. Within this function is a nested function called inner
which prints “Hello” to the console. It then calls the function passed into hello
, along with its arguments, if any.
<span class="lineno">1</span> <span class="nd">@hello</span>
<span class="lineno">2</span> <span class="k">def</span> <span class="nf">foo</span><span class="p">():</span>
<span class="lineno">3</span> <span class="k">print</span><span class="p">(</span><span class="s">"Sweet Charlie"</span><span class="p">)</span>
I chose inner
for the nested function arbitrarily. You can use any name you like. Now, when we call foo
we will get the following output:
In [1]: def hello(func):
...: def inner(*args, **kwargs):
...: print("Hello")
...: return func(*args, **kwargs)
...: return inner
...: @hello
...: def foo():
...: print("Sweet Charlie")
...: foo()
...:
Hello
Sweet Charlie
Now, for any function decorated with @hello
, The string “Hello” will be printed to the console. We can build a more complex – and useful – example on top of this decorator.
Use Case: Logging with Decorators
Let’s create a logging decorator, which will print the name of the called function, along with any parameters passed to it:
<span class="lineno"> 1</span> <span class="k">def</span> <span class="nf">logger</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
<span class="lineno"> 2</span> <span class="k">def</span> <span class="nf">inner</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="lineno"> 3</span> <span class="n">res</span> <span class="o">=</span> <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="lineno"> 4</span> <span class="k">print</span> <span class="n">func</span><span class="o">.</span><span class="n">__name__</span><span class="p">,</span> <span class="n">args</span><span class="p">,</span> <span class="n">kwargs</span>
<span class="lineno"> 5</span> <span class="k">return</span> <span class="n">res</span>
<span class="lineno"> 6</span> <span class="k">return</span> <span class="n">inner</span>
<span class="lineno"> 7</span>
<span class="lineno"> 8</span> <span class="nd">@logger</span>
<span class="lineno"> 9</span> <span class="k">def</span> <span class="nf">do_something</span><span class="p">(</span><span class="n">foo</span><span class="p">,</span> <span class="n">bar</span><span class="p">):</span>
<span class="lineno">10</span> <span class="k">pass</span>
Console output:
>>> do_something(1,2)
do_something (1, 2) {}
Our logger
function, upon a call to do_something(1,2)
simply printed out the function name do_something
and its arguments as the tuple (1,2)
. No keyword (named) arguments were supplied, so the empty dictionary {}
was printed.
Replacing the positional arguments with keyword arguments to do_something
emits the following log statement:
>>> do_something(foo=1,bar=2)
do_something () {'foo': 1, 'bar': 2}
Now, anytime you want to do a little poor man’s debugging you can use the @log
decorator without having to modify any of your existing code. Consider a more robust version of this logger using the built in logging
module.
Use Case: Timing a Function
We can implement another handy tool to measure the performance of a function.
<span class="lineno">1</span> <span class="k">def</span> <span class="nf">timeit</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
<span class="lineno">2</span> <span class="sd">"""A decorator that prints the time a function takes to execute."""</span>
<span class="lineno">3</span> <span class="kn">import</span> <span class="nn">time</span>
<span class="lineno">4</span> <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="lineno">5</span> <span class="n">t</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span>
<span class="lineno">6</span> <span class="n">res</span> <span class="o">=</span> <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="lineno">7</span> <span class="k">print</span> <span class="n">func</span><span class="o">.</span><span class="n">__name__</span><span class="p">,</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span> <span class="o">-</span> <span class="n">t</span>
<span class="lineno">8</span> <span class="k">return</span> <span class="n">res</span>
<span class="lineno">9</span> <span class="k">return</span> <span class="n">wrapper</span>
Let’s simulate a slow-running function:
<span class="lineno">1</span> <span class="kn">import</span> <span class="nn">time</span>
<span class="lineno">2</span>
<span class="lineno">3</span> <span class="nd">@timeit</span>
<span class="lineno">4</span> <span class="k">def</span> <span class="nf">slow</span><span class="p">():</span>
<span class="lineno">5</span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
When we call slow
we introduce an artificial delay of 5 seconds using sleep(5)
:
<span class="o">>>></span> <span class="n">slow</span><span class="p">()</span>
<span class="n">slow</span> <span class="mf">5.00087594986</span>
<span class="o">>>></span> <span class="n">slow</span><span class="p">()</span>
<span class="n">slow</span> <span class="mf">5.0011920929</span>
<span class="o">>>></span> <span class="n">slow</span><span class="p">()</span>
<span class="n">slow</span> <span class="mf">5.00118708611</span>
The operating system’s high resolution timer shows that “5 seconds” can vary just a bit!
Use Case: Stacking Multiple Decorators
Multiple decorators can be applied to any function. Simply stack them like this:
<span class="lineno">1</span> <span class="nd">@decorator3</span>
<span class="lineno">2</span> <span class="nd">@decorator2</span>
<span class="lineno">3</span> <span class="nd">@decorator1</span>
<span class="lineno">4</span> <span class="k">def</span> <span class="nf">foo</span><span class="p">():</span>
<span class="lineno">5</span> <span class="k">pass</span>
It is important to note that Python will apply the decorators from innermost to outermost. In our example above, decorator1
is executed first.
Standard Library Decorators
The Python standard library has a number of built-in decorators, available anywhere.
@classmethod
Class methods are bound to a class, but not to a specific instance of a class. For example:
<span class="lineno">1</span> <span class="k">class</span> <span class="nc">Circle</span><span class="p">:</span>
<span class="lineno">2</span> <span class="n">diameter</span> <span class="o">=</span> <span class="mi">12</span>
<span class="lineno">3</span>
<span class="lineno">4</span> <span class="nd">@classmethod</span>
<span class="lineno">5</span> <span class="k">def</span> <span class="nf">get_diameter</span><span class="p">(</span><span class="n">cls</span><span class="p">):</span>
<span class="lineno">6</span> <span class="k">return</span> <span class="n">cls</span><span class="o">.</span><span class="n">diameter</span>
In the Circle
class above, notice the diameter
variable is a class member. That is, it belongs to the class, not any particular instance. There is also no self.
prepended to diameter
.
The classmethod
decorator applies the same structure to a function. The get_diameter
function is a member of the Circle
class. Notice the first parameter is called cls
. This is an implicit reference to the Circle
class.
We can call get_diameter
as follows:
>>> Circle.get_diameter()
12
Notice we called get_diameter
directly on the Circle
class, and not on an instance.
We can create an instance of Circle
and call get_diameter
, producing the same result:
>>> circle = Circle()
>>> circle.get_diameter()
12
Using classmethod
also has the benefit of working with inheritance. All subclasses will be able to access the function decorated with classmethod
.
Here’s an example of a class method being invoked by a subclass:
<span class="lineno">1</span> <span class="k">class</span> <span class="nc">Parent</span><span class="p">:</span>
<span class="lineno">2</span> <span class="n">member</span> <span class="o">=</span> <span class="s">'x'</span>
<span class="lineno">3</span>
<span class="lineno">4</span> <span class="nd">@classmethod</span>
<span class="lineno">5</span> <span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="n">cls</span><span class="p">):</span>
<span class="lineno">6</span> <span class="k">print</span><span class="p">(</span><span class="n">cls</span><span class="o">.</span><span class="n">member</span><span class="p">)</span>
<span class="lineno">7</span>
<span class="lineno">8</span> <span class="k">class</span> <span class="nc">Child</span><span class="p">(</span><span class="n">Parent</span><span class="p">):</span>
<span class="lineno">9</span> <span class="k">pass</span>
We can call foo
directly from the Child
class:
>>> Child.foo()
x
@staticmethod
Static methods in Python behave a bit differently than class methods, and are more similar to those found in Java or C++, for example. Unlike class methods. static methods have no knowledge of the class in which they are defined.
<span class="lineno">1</span> <span class="k">class</span> <span class="nc">Foo</span><span class="p">:</span>
<span class="lineno">2</span> <span class="nd">@staticmethod</span>
<span class="lineno">3</span> <span class="k">def</span> <span class="nf">bar</span><span class="p">():</span>
<span class="lineno">4</span> <span class="k">pass</span>
Notice that bar
has no cls
or self
arguments. Call static methods similarly to class methods:
>>> Foo.bar()
@property
The property
decorator can be used to control access to a variable. Applying this decorator gives a function getter
, setter
, and deleter
attributes. You can use just the property
decorator on its own:
<span class="lineno">1</span> <span class="k">class</span> <span class="nc">Foo</span><span class="p">:</span>
<span class="lineno">2</span> <span class="k">def</span> <span class="nf">__init</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="lineno">3</span> <span class="bp">self</span><span class="o">.</span><span class="n">_bar</span> <span class="o">=</span> <span class="bp">None</span>
<span class="lineno">4</span>
<span class="lineno">5</span> <span class="nd">@property</span>
<span class="lineno">6</span> <span class="k">def</span> <span class="nf">bar</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="lineno">7</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_bar</span>
This makes the bar
function act as if it were a property:
>>> f = Foo()
>>> f.bar = 'x'
>>> f.bar
'x'
or you can enhance the setter
and deleter
behavior:
<span class="lineno"> 1</span> <span class="k">class</span> <span class="nc">Foo</span><span class="p">:</span>
<span class="lineno"> 2</span> <span class="k">def</span> <span class="nf">__init</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="lineno"> 3</span> <span class="bp">self</span><span class="o">.</span><span class="n">_bar</span> <span class="o">=</span> <span class="bp">None</span>
<span class="lineno"> 4</span>
<span class="lineno"> 5</span> <span class="nd">@property</span>
<span class="lineno"> 6</span> <span class="k">def</span> <span class="nf">bar</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="lineno"> 7</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_bar</span>
<span class="lineno"> 8</span>
<span class="lineno"> 9</span> <span class="nd">@bar.setter</span>
<span class="lineno">10</span> <span class="k">def</span> <span class="nf">bar</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="lineno">11</span> <span class="bp">self</span><span class="o">.</span><span class="n">_bar</span> <span class="o">=</span> <span class="n">value</span>
<span class="lineno">12</span>
<span class="lineno">13</span> <span class="nd">@bar.deleter</span>
<span class="lineno">14</span> <span class="k">def</span> <span class="nf">bar</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="lineno">15</span> <span class="k">del</span> <span class="bp">self</span><span class="o">.</span><span class="n">_bar</span>
Notice the syntax of setter
and deleter
– the decorator is prefixed with bar.
. This indicates that the decorator applies to the property bar
. Decorated functions can also have decorators!
Conclusion
We’ve seen how decorators can be used to augment behavior with minimal code changes. This approach has limitless uses. Please leave a comment below, and let me know how you use decorators!