Follow Techotopia on Twitter

On-line Guides
All Guides
eBook Store
iOS / Android
Linux for Beginners
Office Productivity
Linux Installation
Linux Security
Linux Utilities
Linux Virtualization
Linux Kernel
System/Network Admin
Programming
Scripting Languages
Development Tools
Web Development
GUI Toolkits/Desktop
Databases
Mail Systems
openSolaris
Eclipse Documentation
Techotopia.com
Virtuatopia.com

How To Guides
Virtualization
General System Admin
Linux Security
Linux Filesystems
Web Servers
Graphics & Desktop
PC Hardware
Windows
Problem Solutions

  




 

 

Advanced Parameter Handling For Functions

In the section called “More Features” we hinted that Python functions can handle a variable number of argument values in addition to supporting optional argument values. Earlier, when we defined a function that had optional parameters, it had a definite number of parameters, but some (or all) could be omitted because we provided default values for them.

If we provide too many positional parameters to a function, Python raises an exception. Consider the following example. We defined a function of three positional parameters, and then evaluated it with more than three argument values.

>>> 
def avg(a,b,c):
        return (a+b+c)/3.0


>>> 
avg(10,11,12)

11.0
>>> 
avg(10,11,12,13,14,15,16)


Traceback (most recent call last):
  File "<pyshell#16>", line 1, in -toplevel-
    avg(10,11,12,13,14,15,16)
TypeError: avg() takes exactly 3 arguments (7 given)

Unlimited Number of Positional Argument Values

Python lets us define a function that handles an unknown and unlimited number of argument values. Examples of built-in functions with a unlimited number of argument values are max and min.

Rather than have Python raise an exception, we can request the additional positional argument values be collected into a tuple. To do this, you provide a final parameter definition of the form * extras . The * indicates that this parameter variable is the place to capture the extra argument values. The variable, here called extras , will receive a sequence with all of the extra positional argument values.

You can only provide one such variable (if you provided two, how could Python decide which of these two got the extra argument values?) You must provide this variable after the ordinary positional parameters in the function definition.

Examples of Unlimited Positional Arguments. The following function accepts an unlimited number of positional arguments; it collects these in a single tuple parameter, args.

def myMax( *args ):
    max= arg[0]
    for a in args[1:]:
        if a > max: max= a
    return max

We take the first element of the args tuple as our current guess at the maximum value, max. We use a for loop that will set the variable a to each of the other arguments. If a is larger than our current guess, we update the current guess, max. At the end of the loop, the post condition is that we have visited every element in the list args; the current guess must be the largest value.

Here's another example. In this case we have a fixed parameter in the first position and all the extra parameters collected into a tuple called vals.

def printf( format, *vals ):
    print format % vals

This should look familiar to C programmers. Now we can write the following, which may help ease the transition from C to Python.

printf( "%s = %d", "some string", 2 )
printf( "%s, %s, %d %d", "thing1", "thing2", 3, 22 )

Unlimited Number of Keyword Argument Values

In addition to collecting extra positional argument values into a single parameter, Python can also collect extra keyword argument values into a dict. If you want a container of keyword arguments, you provide a parameter of the form ** extras . Your variable, here called extras , will receive a dict with all of the keyword parameters.

The following function accepts any number opf keyword arguments; they are collected into a single parameter.

def rtd( **args ):
    if args.has_key( "rate" ) and args.has_key( "time" ):
        args["distance"]= args["rate"]*args["time"]
    elif args.has_key( "rate" ) and args.has_key( "distance" ):
        args["time"]= args["distance"]/args["rate"]
    elif args.has_key( "time" ) and args.has_key( "distance" ):
        args["rate"]= args["distance"]/args["time"]
    else:
        raise Exception("%r does not compute" % ( args, ) )
    return args

Here's two examples of using this rtd function.

>>>

print rtd( rate=60, time=.75 )

{'distance': 45.0, 'rate': 60.0, 'time': 0.75}
>>>

print rtd( distance=173, time=2+50/60.0 )

{'distance': 173, 'rate': 61.058823529411761, 
 'time': 2.8333333333333335}
            

The keyword arguments are collected into a dict, named args. We check for some combination of "rate", "time" and "distance" by using the dict method has_key. If the dict of keyword arguments, args, has the given keyword, has_key returns true. For each combination, we can solve for the remaining value and update the dict by insert the additional key and value into the dict.

Using a Container Instead of Individual Arguments

We can also force a sequence to be broken down into individual parameters. We can use a special version of the * operator when evaluating a function. Here's an example of forcing a 3-tuple to be assigned to three positional parameters.

>>> 
def avg3(a,b,c):
...     return (a+b+c)/3.0


>>> 
avg3( *(4,5,7) )

5.333333333333333

In this example, we told Python to break down our 3-tuple, and assign each value of the tuple to a separate parameter variable.

As with the * operator, we can use ** to make a dict become a series of keyword parameters to a function.

>>> d={ 'a':5, 'b':6, 'c':9 }
>>> avg3( **d )
6.666666666666667

In this example, we told Python to assign each element of the dict, d, to a parameter of our function.

We can mix and match this with ordinary parameter assignment, also. Here's an example.

>>> avg3( 2, b=3, **{'c':4} )
3.0

Here we've called our function with three argument values. The parameter a will get its value from a simple positional parameter. The parameter b will get its value from a keyword argument. The parameter c will get its value from having the dict {'c':4} turned into keyword parameter assignment.

We'll make more use of this in the section called “Inheritance”.

Creating a print function

The print statement has irregular, complex syntax. Also, because it's a statement, making blanket changes requires tedious, manual search and replace on the program source. Writing a print function can make the print statement slightly easier to cope with.

We want to mirror the capabilities of the existing print statement, so we need to accept an unlimited number of positional parameters, as well as some optional keyword parameters. One keyword parameter can be used in place of the "chevron" feature to define the output file. The other keyword parameters can provide formatting information: what character goes between fields and what character (if any) goes at the end of the line.

We want to have a syntax summary like this.

_print( args , sep= string , end= string , file= file )

Converts the argument values to strings, and writes them to the given file. If no file is provided, writes to standard output. The sep parameter is the column separator, which is a space by default. The end parameter is the end-of-line character, which is a \n by default.

A simple version of our print replacement function would look something like the following. This example looks forward to using the map function, which we won't look at in detail until the section called “Sequence Processing Functions: map, filter, reduce and zip.

Example 15.1. printFunction.py

import sys
def _print( *args, **kw ):
    out= kw.get('file',sys.stdout)
    linesep= kw.get('end','\n')
    colsep= kw.get('sep',' ')
    out.write( colsep.join( map(str,args) ) )
    out.write( linesep )

Our intent is to have a function that we can use as follows. We want to provide a long sequence of positional parameters first, followed by some optional keyword parameters.

_print( "Python Version", end="" )
_print( sys.version )
_print( "This is", "Stderr", out=sys.stderr, )
_print( "This is", "Stdout", out=sys.stdout, )

Here are the original print statements that we replaced.

print "Python Version", 
print sys.version
print >>sys.stderr, "This is", "Stderr"
prinr >>sys.stdout, "This is", "Stdout"

Note that we can't simply say def _print ( *args, sep=' ', end='\n', file=None ) as the definition of this function. We can't have the "all extra positional parameters" listed first in the function definition, even though this is our obvious intent. To achieve the syntax we want, we have to write a function which collects all positional parameters into one sequence of values, and all keyword parameters into a separate dictionary.


 
 
  Published under the terms of the Open Publication License Design by Interspire