One of the coolest features of Python is its nice ability to create decorators. In short, decorators allow us to modify how a function behaves without changing the function’s source code. This can often make code cleaner and easier to modify. For instance, decorators are also really useful if you have a collection of functions where each function has similar repeating code. Fortunately, decorators can now also be created in R!
The first example below is in Python – we’ll get to R in a moment. If you already know about decorators in Python, feel free to skip below to the R section.
Below we have a function that prints today’s date. In our example, we create two functions. The first – print_start_end takes another function as input. It prints “Starting function call…” prior to calling this input function. Secondly, it calls the function. Lastly, it prints “Finished function call…”.
The other function, todays_date, simply prints today’s date.
import datetime # defining a decorator def print_start_end(f): def wrapper(**args): print("Starting function call...") f() print("Finished function call...") return wrapper def todays_date(): print(datetime.datetime.today())
Now, let’s say we want to call our todays_date function, but would like to wrap it inside the print_start_end function. One way to do that is by having a nested function call:
print_start_end(todays_date)
However, we can also add a decorator to the todays_date like below. In Python, a decorator is created by adding an “@” followed by a function name (the “decorating” function).
@print_start_end def todays_date(): print(datetime.datetime.today())
The result of running todays_date() now is below:
We get the same result using a decorator as we did using a nested function call above. The nice part of this, however, is that we don’t have to have any nested functions or change the code within todays_date. We can also easily comment out the decorator to turn off this functionality if we wanted to.
#@print_start_end def todays_date(): print(datetime.datetime.today())
There’s a whole lot more to decorators. To learn more, check out Powerful Python, or Guide to Learning Python Decorators. The second of these resources focuses solely on decorators, while the first one also covers several other Python features.
Now, let’s walk through how to create decorators in R. We can do that using an awesome package called tinsel.
To get started, we first need to install tinsel, which requires devtools. If you don’t have the devtools package installed, you’ll need to install that first (install.packages(“devtools”)). Then, you can install tinsel by running the command below.
devtools::install_github('nteetor/tinsel')
Once you’re setup, you can start decorating functions. Let’s recreate the example above using R. The first function is straightforward to reproduce – we’re just changing Python syntax to R.
library(tinsel) print_start_end <- function(f) { wrapper <- function(...) { print("Starting function call...") f() print("Finished function call...") } return(wrapper) }
Now when it comes to the function we’re decorating – todays_date, we add our decorator using “#.” plus the name of our decorator function – in this case, print_start_end. Essentially, the main difference between creating a decorator here in R vs. Python is that we use “#.” rather than an “@” in the line above a function to signify that we want to decorate that function.
#. print_start_end todays_date <- function() { print(Sys.Date()) }
In order for R to recognize that we want to use decorators, we need to source our R script using tinsel’s source_decoratees function. Here we just need to pass the name of our script.
source_decoratees("test_dec.R")
Now when we call todays_date, the decorated version of the function gets executed.
As mentioned, one key use of decorators is to handle collections of functions that have similar repeating lines of code. For example, let’s apply the print_start_end decorator above to two other functions.
#. print_start_end yesterday <- function() { print(Sys.Date() - 1) } #. print_start_end tomorrow <- function() { print(Sys.Date() + 1) }
Another nice feature of decorators is that if we make changes to the decorator, we can easily apply these to any of the functions being decorated. For example, below we can change our print_start_end function to time the execution of a function.
print_start_end <- function(f) { wrapper <- function(...) { start <- proc.time() f() print(proc.time() - start) } return(wrapper) }
Then, we can call our functions like below with our updated decorator. This allows us to make changes once rather than for each function.
yesterday() tomorrow()
That’s all for now! Please click to follow my blog on Twitter and keep up with my latest posts, or check out some additional resources for learning Python and R by clicking here.
For more on tinsel, see here.
Very excited to announce the early-access preview (MEAP) of my upcoming book, Software Engineering for…
Ever had long-running code that you don't know when it's going to finish running? If…
Background If you've done any type of data analysis in Python, chances are you've probably…
In this post, we will investigate the pandas_profiling and sweetviz packages, which can be used…
In this post, we're going to cover how to plot XGBoost trees in R. XGBoost…
In this post, we'll discuss the underrated Python collections package, which is part of the…