COMP1531

🐶Software Engineering

7.3 - Design - Decorators

 

Author: Hayden Smith 2021

In this lecture

Why?

  • Writing clean and well designed code has huge benefits we've discussed previously, so let's learn some more

What?

  • Decorators

 

Decorators

Decorators allow us to add functionality to a function without altering the function itself, by "decorating" (wrapping) around it.

 

But first... some background

Function Arguments

def foo1(zid, name, age, suburb):
    print(zid, name, age, suburb)

def foo2(zid=None, name=None, age=None, suburb=None):
    print(zid, name, age, suburb)

if __name__ == '__main__':
    
    foo1('z3418003', 'Hayden', '72', 'Kensington')
    
    foo2('z3418003', 'Hayden')
    foo2(name='Hayden', suburb='Kensington', age='72', zid='z3418003')
    foo2(age='72', zid='z3418003')
    
    foo2('z3418003', suburb='Kensington')

decor1.py

Arguments in python can either be keyword arguments (named) or non-keyword arguments.

 

Non-keyword arguments cannot appear after keyword arguments in the argument list

Function Arguments

def foo(zid=None, name=None, *args, **kwargs):
    print(zid, name)
    print(args) # A list
    print(kwargs) # A dictionary

if __name__ == '__main__':
    foo('z3418003', None, 'mercury', 'venus', planet1='earth', planet2='mars')

decor2.py

def foo(*args, **kwargs):
    print(args) # A list
    print(kwargs) # A dictionary

if __name__ == '__main__':
    foo('this', 'is', truly='dynamic')

decor3.py

We can use a generalised method of capturing:

  • *args: non-keyword arguments as a list
  • *kwargs: keyword arguments as a dictionary

 

Decorators: First principles

def make_uppercase(input):
	return input.upper()

def get_first_name():
	return "Hayden"

def get_last_name():
	return "Smith"

if __name__ == '__main__':
    print(make_uppercase(get_first_name()))
    print(make_uppercase(get_last_name()))

decor4.py

Consider "make_uppercase" to be a decorator function. It allows you to add functionality to the get first name function without altering the function.

A proper decorator

def make_uppercase(function):
	def wrapper(*args, **kwargs):
		return function(*args, **kwargs).upper()
	return wrapper

@make_uppercase
def get_first_name():
	return "Hayden"

@make_uppercase
def get_last_name():
	return "Smith"

if __name__ == '__main__':
    print(get_first_name())
    print(get_last_name())

decor5.py

This code can be used as a template

Now let's generalise it with the proper python decorator syntax.

Decorator, run twice

def run_twice(function):
	def wrapper(*args, **kwargs):
		return function(*args, **kwargs) \
		     + function(*args, **kwargs)
	return wrapper

@run_twice
def get_first_name():
	return "Hayden"

@run_twice
def get_last_name():
	return "Smith"

if __name__ == '__main__':
    print(get_first_name())
    print(get_last_name())

decor6.py

Decorator, more

class Message:
	def __init__(self, id, text):
		self.id = id
		self.text = text

messages = [
	Message(1, "Hello"),
	Message(2, "How are you?"),
]

def get_message_by_id(id):
	return [m for m in messages if m.id == id][0]

def message_id_to_obj(function):
	def wrapper(*args, **kwargs):
		argsList = list(args)
		argsList[0] = get_message_by_id(argsList[0])
		args = tuple(argsList)
		return function(*args, **kwargs)
	return wrapper

@message_id_to_obj
def printMessage(message):
	print(message.text)

if __name__ == '__main__':
	printMessage(1)

decor7.py

Feedback

COMP1531 21T3 - 7.3 - SDLC Design - Writing good software Part 2

By haydensmith

COMP1531 21T3 - 7.3 - SDLC Design - Writing good software Part 2

  • 1,538