Decorator Basics and Class Decorators

Decorator Overview

Decorators provide a simple syntax for calling higher-order functions. By definition, a decorator is a function that takes another function and extends the behavior of the latter function without explicitly modifying it. Sounds confusing—but it’s really not, especially after we go over a number of examples.

A simple example

def wrapper(f):
    def inner_func(*args,**kwargs):
        start_time = time.time()
        c =  f(*args,**kwargs)
        end_time = time.time()
        print(end_time - start_time)
        return c
    return inner_func

@wrapper
def add(x,y):
    return x + y

@wrapper
def subtract(x,y):
    return  x - y

sum = add(5,10)
print(sum)

A decorator is something that makes your code look clean and well written. In OOP decorators are common place. There are a few decorators that are provided out of the box from python. They are listed below.

Decorators are elegant! Makes code look cleaner and trimmer

Out-of the box function decorators that can come in handy

  • @property
  • @classmethod
  • @staticmethod

@property -Getters, setters, deleters

  • The property decorator allows us to define Class methods that we can access like attributes. This allows us to implement getters, setters, and deleters
class Employee:

    def __init__(self, first, last):
        self.first = first
        self.last = last

    @property
    def email(self):
        return '{}.{}@email.com'.format(self.first, self.last)

    @property
    def fullname(self):
        return '{} {}'.format(self.first, self.last)


emp_1 = Employee('John', 'Smith')

print(emp_1.first)
print(emp_1.email)
print(emp_1.fullname)

@classmethod

  • Classmethods takes cls as a parameter instead of self
  • Classmethods don’t modify the state of the instance they modify class state and this change applies across all instances.

A typical example of a function generated by @classmethod decorator

@classmethod
def increase_count(cls,counter):
    cls.counter += counter

@staticmethod

  • Staticmethod is a method that takes no parameter but is still is a logical part of the class this method is neither bound by an instance or a class
@staticmethod
def is_holiday(day):
    if day.weekday() == 5 or day.weekday() == 6:
        return True
    return False

A combined example is shown below

#Class Method as a constructor


import datetime

class DateTimeParser:
    counter = 0
    def __init__(self,d=None,m=None,y=None):
        self.d, self.m,self.y = d,m,y


    @classmethod
    def parse(cls,datestring):
        date,month,year = datestring.split("/")
        return cls(date,month,year)

    @classmethod
    def increase_count(cls,counter):
        cls.counter += counter


    @staticmethod
    def is_holiday(day):
        if day.weekday() == 5 or day.weekday() == 6:
            return True
        return False


if __name__ == "__main__":
    date1 = DateTimeParser.parse("12/05/2015")
    print(DateTimeParser().counter)
    print(date1.counter)

    DateTimeParser().increase_count(10)
    print(date1.counter)
    print(DateTimeParser().counter)

Class Decorators

Class decorators are similar to function decorators, but they are run at the end of a class statement to rebind a class name to a callable. As such, they can be used to either manage classes just after they are created, or insert a layer of wrapper logic to manage instances when they are later created.
def decorator(cls): # On @ decoration
     class Proxy:
         def __init__(self, *args): # On instance creation: make a cls
             self.wrapped = cls(*args)
         def __getattr__(self, name): # On attribute fetch: extra ops
             return getattr(self.wrapped, name)
         return Proxy

@decorator
class C:
    pass

x = C()

Note

Work in progress