MCS 275 Spring 2022
Emily Dumas
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.
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".
Sometimes you may write a function that needs to pass most of its arguments on to another function.
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)
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
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
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)
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.
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
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.
@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..
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.