Fun With Python Function Parameters

Virtually every programming language has functions and procedures, a way of separating out a block of code that can be called many times from different places in your program, and a way to pass parameters into them. Python is no different, so we’ll quickly run over the standard stuff that most languages have, then take a look at some of the cool stuff Python has to offer.

Positional Function Parameters in Python

Here’s a really simply function:

def foo(val1, val2, val3):
    return val1 + val2 + val3

When used, we get the following:

>>> print(foo(val1=10, val2=3))
7
>>> # Note: parameters are in a different order
>>> print(foo(val2=3, val1=10))
7

Unusually, Python also allows positional parameters to be specified by name:

def foo(val1, val2):
    ''' Note: parameters are positional, not named. '''
    return val1 – val2

When used, we get the following:

>>> # But we can still set them by name
>>> print(foo(val1=10, val2=3))
7
>>> # And in any order!
>>> print(foo(val2=3, val1=10))
7

Here’s a more complex example:

def foo(p1, p2, p3, n1=None, n2=None):
    print('[%d %d %d]' % (p1, p2, p3))
    if n1:
        print('n1=%d' % n1)
    if n2:
        print('n2=%d' % n2)

When used, we get the following:

>>> foo(1, 2, 3, n2=99)
[1 2 3]
n2=99
>>> foo(1, 2, n1=42, p3=3)
[1 2 3]
n1=42

This seems really confusing, but the key to understanding how it works is to recognize that the function’s parameter list is a dictionary (a set of key/value pairs). Python matches up the positional parameters first, then assigns any named parameters specified in the function call.

Variable Python Function Parameter Lists

The Python coolness really kicks in when you start to look at variable parameter lists. You can write your functions without even knowing what parameters will be passed in!

In the function below, the asterisk in front of the vals parameter means any other positional parameters.

def lessThan(cutoffVal, *vals) :
    ''' Return a list of values less than the cutoff. '''
    arr = []
    for val in vals :
        if val < cutoffVal:
            arr.append(val)
    return arr

When using the function, we get:

>>> print(lessThan(10, 2, 17, -3, 42))
[2, -3]

The first positional value we specify in the function call (10) is given to the first parameter in the function (cutoffVal), then all the remaining positional values are put in a tuple and assigned to vals. We then iterate through these values, looking for any that are less than the cutoff value.

We can also do the same kind of thing with named parameters. A double asterisk in front of the dict parameter below means any other named parameters. This time, Python will give them to us as key/value pairs in a dictionary.

def printVals(prefix='', **dict):
    # Print out the extra named parameters and their values
    for key, val in dict.items():
        print('%s [%s] => [%s]' % (prefix, str(key), str(val)))

When using the function, we get:

>>> printVals(prefix='..', foo=42, bar='!!!')
[foo] => [42]
[bar] => [!!!]
>>> printVals(prefix='..', one=1, two=2)
[two] => [2]
[one] => [1]

Note in the last example that the values are not printed out in the same order that they were specified in the function call. This is because these extra named parameters are passed through in a dictionary, which is an un-ordered data structure.

A Real World Example

So, what would you use all this for? As an example, it’s common for programs to generate messages from a template that has placeholders for values that will be inserted at run-time. For example:

Hello {name}. Your account balance is {1}, you have {2} available credit.

The function below takes such a template and a set of parameters that will be used to replace the placeholders.

def formatString(stringTemplate, *args, **kwargs):
    # Replace any positional parameters
    for i in range(0, len(args)):
        tmp = '{%s}' % str(1+i)
        while True:
            pos = stringTemplate.find(tmp)
            if pos < 0:
                break
            stringTemplate = stringTemplate[:pos] + \
                             str(args[i]) + \
                             stringTemplate[pos+len(tmp):]
 
    # Replace any named parameters
    for key, val in kwargs.items():
        tmp = '{%s}' % key
        while True:
            pos = stringTemplate.find(tmp)
            if pos < 0:
                break
            stringTemplate = stringTemplate[:pos] + \
                             str(val) + \
                             stringTemplate[pos+len(tmp):]
 
    return stringTemplate

Here it is in action:

>>> stringTemplate = 'pos1={1} pos2={2} pos3={3} foo={foo} bar={bar}'
>>> print(formatString(stringTemplate, 1, 2))
pos1=1 pos2=2 pos3={3} foo={foo} bar={bar}
>>> print(formatString(stringTemplate, 42, bar=123, foo='hello'))
pos1=42 pos2={2} pos3={3} foo=hello bar=123

Leave a Reply

Your email address will not be published. Required fields are marked *