Decorator
A decorator is an object which holds a reference to another object that you want to alter/extend the behavior of another object without inheriting from it.
python
from abc import ABC
class Shape(ABC):
def __str__(self):
return ''
class Circle(Shape):
def __init__(self, radius=0.0):
self.radius = radius
def resize(self, factor):
self.radius *= factor
def __str__(self):
return f'A circle of radius {self.radius}'
class Square(Shape):
def __init__(self, side):
self.side = side
def __str__(self):
return f'A square with side {self.side}'
class ColoredShape(Shape):
def __init__(self, shape, color):
if isinstance(shape, ColoredShape):
raise Exception('Cannot apply ColoredDecorator twice')
self.shape = shape
self.color = color
def __str__(self):
return f'{self.shape} has the color {self.color}'
class TransparentShape(Shape):
def __init__(self, shape, transparency):
self.shape = shape
self.transparency = transparency
def __str__(self):
return f'{self.shape} has {self.transparency * 100.0}% transparency'
if __name__ == '__main__':
circle = Circle(2)
print(circle)
red_circle = ColoredShape(circle, "red")
print(red_circle)
# ColoredShape doesn't have resize()
# red_circle.resize(3)
red_half_transparent_square = TransparentShape(red_circle, 0.5)
print(red_half_transparent_square)
# nothing prevents double application
mixed = ColoredShape(ColoredShape(Circle(3), 'red'), 'blue')
print(mixed)
Dynamic Decorator
You can also have a Dynamic Decorator by passing calls to access the underlying item.
python
class FileWithLogging:
def __init__(self, file):
self.file = file
def writelines(self, strings):
self.file.writelines(strings)
print(f'wrote {len(strings)} lines')
def __iter__(self):
return self.file.__iter__()
def __next__(self):
return self.file.__next__()
def __getattr__(self, item):
return getattr(self.__dict__['file'], item)
def __setattr__(self, key, value):
if key == 'file':
self.__dict__[key] = value
else:
setattr(self.__dict__['file'], key)
def __delattr__(self, item):
delattr(self.__dict__['file'], item)
if __name__ == '__main__':
file = FileWithLogging(open('hello.txt', 'w'))
file.writelines(['hello', 'world'])
file.write('testing')
file.close()
Functional Decorators
Functional decorators are built in to python to alter the behavior of your functions.
python
import time
def time_it(func):
def wrapper():
start = time.time()
result = func()
end = time.time()
print(f'{func.__name} took {int(end-start) * 1000}ms')
return result
return wrapper
@time_it
def some_op():
print('Starting op')
time.sleep(1)
print('Finished op')
return 123
if __name__ == '__main__':
some_op()