# Lecture 8

MCS 275 Spring 2022
David Dumas

### Lecture 8: Variadic functions and decorators

Course bulletins:
• Project 1 due Fri 4 Feb at 6:00pm central.
• Project 1 autograder opens on Monday.
• Homework 3 available.

A function is variadic if it can accept a variable number of arguments. This is general CS terminology.

Python supports these. The syntax

def f(a,b,*args):
means that the first argument goes into variable a, the second into variable b, and any other arguments are put into a tuple which is assigned to args

The syntax

def f(a,b,**kwargs):
or
def f(a,b,*args,**kwargs):
puts extra keyword arguments into a dictionary called kwargs.

It is traditional to use the names args and kwargs, but it is not required.

## Argument unpacking

Take arguments from a list or tuple:

L = [6,11,16]
f(1,*L) # calls f(1,6,11,16)

Take keyword arguments from a dict:

d = { "mcs275": "fun", "x": 42 }
f(1,z=0,**d) # calls f(1,z=0,mcs275="fun",x=42)

Think of * as "remove the brackets", and ** as "remove the curly braces".

## Why?

Sometimes you may write a function that needs to pass most of its arguments on to another function.

## Function arguments

Functions in Python can accept functions as arguments.

def dotwice(f):
"""Call function f twice"""
f()
f()

A better version works with functions that accept arguments:

def dotwice(f,*args,**kwargs):
"""Call function f twice (allowing arguments)"""
f(*args,**kwargs)
f(*args,**kwargs)

## Returning functions

Functions in Python can return functions. Often this is used with a return value that is a defined inside the function body, making a "function factory".

def return_power(n):
def inner(x): # function inside a function!
"""Raise x to a power"""
return x**n
return inner

## Modifying functions

def return_twice_doer(f):
"""Return a new function which calls f twice"""
def inner(*args,**kwargs):
"""Call a certain function twice"""
f(*args,**kwargs)
f(*args,**kwargs)
return inner

## Replacing functions

In some cases we might want to replace an existing function with a modified version of it (e.g. as returned by some other function).

def g(x):
"""Print the argument with a message"""
print("Function got value",x)

# actually, I wanted to always print that message twice!
g = return_twice_doer(g)

## Decorator syntax

There is a shorter syntax to replace a function with a modified version.

@modifier
def fn(x,y):
"""Function body goes here"""


is equivalent to

def fn(x,y):
"""Function body goes here"""
fn = modifier(fn)

The symbol @modifier (or any @name) before a function definition is called a decorator.

## Returning values

Usually, the inner function of a decorator should return the value of the (last) call to the argument function.

def return_twice_doer(f):
"""Return a new function which calls f twice"""
def inner(*args,**kwargs):
"""Call a certain function twice"""
f(*args,**kwargs)
return f(*args,**kwargs)
return inner

## Decorator arguments

Python allows @decorator(arg1,arg2,...).

@dec(2)
def printsq(x):
print(x*x)
is equivalent to
thisdec = dec(2)

@thisdec
def printsq(x):
print(x*x)
In other words, if a decorator is given arguments, then the name after @ is expected to be a decorator factory.

## A few built-in decorators

• @functools.lru_cache(100) -- Save arguments and return values for up to 100 recent calls to a function; reuse stored return values when possible. Good for expensive operations.*
• @classmethod -- Make a method a class method (callable from the class itself, gets class as first argument). E.g. for alternate constructors.
• @atexit.register -- Ask that this function be called just before the program exits.

* In Python 3.9+ there is also the simpler functools.cache decorator which stores an unlimited number of past function calls..

## Multiple decorators

Each must be on its own line.

@dec1
@dec2
@dec3
def f(x):
"""Function body goes here"""


replaces f with dec1(dec2(dec3(f))).

So the decorator closest to the function name acts first.

### References

• See Lutz, Chapter 18 for more about function arguments (including variadic functions).
• Beazley & Jones, Chapter 7 has examples of variadic functions.
• See Lutz, Chapter 39 for a detailed discussion of Python decorators.
• See Beazley & Jones, Chapter 9 for several examples of decorators.

### Acknowledgment

• I reviewed course materials created by Danko Adrovic (UIC MSCS faculty member) while preparing a previous version of this lecture.

### Revision history

• 2022-01-26 Initial publication