Decorators with *args, **kwargs
Decorators: Decorators - with *args and **kwargs
This post builds on my previous one about Decorators.
So we have the decorator that 'prettifies' the date returned from the original function.
def pretty_date(func):
def wrapper():
print 'getting the date from function: {}'.format(func.__name__)
date = func()
print 'got the date: {} (before it was formatted)'.format(date)
return '{0:%Y}_{0:%m}_{0:%d}'.format(date)
return wrapper
This @pretty_date
decorator can be used with any function that returns a date, no matter how that date is calculated. As long as it is in a format that the return '{0:%Y}_{0:%m}_{0:%d}'.format(date)
can evaluate.
But what if the original function needs some arguments in order for it to get the date? Yes, those arguments can be passed through the decorator to the function. It's rather simple. Just use *args and **kwargs. Read more about *args and **kwargs.
Let's say we have this function:
from datetime import datetime, timedelta
def date_into_future(date, days):
date_format = "%m/%d/%Y"
a = datetime.strptime(date, date_format)
return a + timedelta(days=days)
It has 2 arguments: a date
and numbers of days
into the future. All it does is add the number of days to the original date. And then later we want to 'prettify' it with the @pretty_date
decorator.
Or we have this function:
from datetime import datetime, timedelta
@pretty_date
def date_in_past(month, day, days, year=2014):
date = '{}/{}/{}'.format(month, day, year)
date_format = "%m/%d/%Y"
a = datetime.strptime(date, date_format)
return a - timedelta(days=days)
This function has 4 arguments. The first 3 are positional, the last is a keyword argument. OK, this is just for argument learning sake. This function gets a date and subtracts a number of days from it. And then of course, we want to 'prettify' it.
These and the other functions from the previous post all can be decorated with the same @pretty_date
decorator. But since they all have different amounts of args and even kwargs, we have to design the decorator to be able to handle all possible functions that might be wrapped by it.
Here's the revised decorator:
def pretty_date(func):
def wrapper(*args, **kwargs):
print 'getting the date from function: {}'.format(func.__name__)
date = func(*args, **kwargs)
print 'the date: {} (before it was formatted)'.format(date)
return '{0:%Y}_{0:%m}_{0:%d}'.format(date)
return wrapper
Notice the *args
and **kwargs
in the def wrapper(*args, **kwargs)
and again in the date = func(*args, **kwargs)
. They are included in the wrapper and then passed to the original function. Which of course uses those arguments to get the date.
And that's it! You can use this decorator with many different functions - whether they do or do not have args
or kwargs
.
Here's how you might call these 2 functions. (Remember, once the functions are wrapped with the decorator, they are always wrapped. So when we call the function, they are automatically decorated.)
print date_into_future('12/8/2014', 30)
print date_in_past(year=2014, month=1, day=10, days=30)
print date_in_past(2, 28, 45, year=2014)
And the results:
getting the date from function: date_into_future
the date: 2015-01-07 00:00:00 (before it was formatted)
2015_01_07
getting the date from function: date_in_past
the date: 2013-12-11 00:00:00 (before it was formatted)
2013_12_11
getting the date from function: date_in_past
the date: 2014-01-14 00:00:00 (before it was formatted)
2014_01_14