Section 11.6. Variable-Length Arguments


11.6. Variable-Length Arguments

There may be situations where your function is required to process an unknown number of arguments. These are called variable-length argument lists. Variable-length arguments are not named explicitly in function declarations because the number of arguments is unknown before runtime (and even during execution, the number of arguments may be different on successive calls), an obvious difference from formal arguments (positional and default), which are named in function declarations. Python supports variable-length arguments in two ways because function calls provide for both keyword and non-keyword argument types.

In Section 11.2.4, we looked at how you can use the * and ** characters in function calls to specify grouped sets of arguments, non-keyword and keyword arguments. In this section, we will see the same symbols again, but this time in function declarations, to signify the receipt of such arguments when functions are called. This syntax allows functions to accept more than just the declared formal arguments as defined in the function declaration.

11.6.1. Non-keyword Variable Arguments (Tuple)

When a function is invoked, all formal (required and default) arguments are assigned to their corresponding local variables as given in the function declaration. The remaining non-keyword variable arguments are inserted in order into a tuple for access. Perhaps you are familiar with "varargs" in C (i.e., va_list, va_arg, and the ellipsis [ ... ]). Python provides equivalent supportiterating over the tuple elements is the same as using va_arg in C. For those who are not familiar with C or varargs, they just represent the syntax for accepting a variable (not fixed) number of arguments passed in a function call.

The variable-length argument tuple must follow all positional and default parameters, and the general syntax for functions with tuple or non-keyword variable arguments is as follows:

def function_name([formal_args,] *vargs_tuple):    "function_documentation_string"    function_body_suite


The asterisk operator ( * ) is placed in front of the variable that will hold all remaining arguments once all the formal parameters if have been exhausted. The tuple is empty if there are no additional arguments given.

As we saw earlier, a TypeError exception is generated whenever an incorrect number of arguments is given in the function invocation. By adding a variable argument list variable at the end, we can handle the situation when more than enough arguments are passed to the function because all the extra (non-keyword) ones will be added to the variable argument tuple. (Extra keyword arguments require a keyword variable argument parameter [see the next section].)

As expected, all formal arguments must precede informal arguments for the same reason that positional arguments must come before keyword arguments.

def tupleVarArgs(arg1, arg2='defaultB', *theRest):     'display regular args and non-keyword variable args'     print 'formal arg 1:', arg1     print 'formal arg 2:', arg1     for eachXtrArg in theRest:         print 'another arg:', eachXtrArg


We will now invoke this function to show how variable argument tuples work:

>>> tupleVarArgs('abc') formal arg 1: abc formal arg 2: defaultB >>> >>> tupleVarArgs(23, 4.56) formal arg 1: 23 formal arg 2: 4.56 >>> >>> tupleVarArgs('abc', 123, 'xyz', 456.789) formal arg 1: abc formal arg 2: 123 another arg: xyz another arg: 456.789


11.6.2. Keyword Variable Arguments (Dictionary)

In the case where we have a variable number or extra set of keyword arguments, these are placed into a dictionary where the "keyworded" argument variable names are the keys, and the arguments are their corresponding values. Why must it be a dictionary? Because a pair of items is given for every argumentthe name of the argument and its valueit is a natural fit to use a dictionary to hold these arguments. Here is the syntax of function definitions that use the variable argument dictionary for extra keyword arguments:

def function_name([ formal_args,][ *vargst,] **vargsd):     function_documentation_string     function_body_suite


To differentiate keyword variable arguments from non-keyword informal arguments, a double asterisk ( ** ) is used. The ** is overloaded so as not to be confused with exponentiation. The keyword variable argument dictionary should be the last parameter of the function definition prepended with the '**'. We now present an example of how to use such a dictionary:

def dictVarArgs(arg1, arg2='defaultB', **theRest):     'display 2 regular args and keyword variable args'     print 'formal arg1:', arg1     print 'formal arg2:', arg2     for eachXtrArg in theRest.keys():         print 'Xtra arg %s: %s' % \             (eachXtrArg, str(theRest[eachXtrArg]))


Executing this code in the interpreter, we get the following output:

>>> dictVarArgs(1220, 740.0, c='grail') formal arg1: 1220 formal arg2: 740.0 Xtra arg c: grail >>> >>> dictVarArgs(arg2='tales', c=123, d='poe', arg1='mystery') formal arg1: mystery formal arg2: tales Xtra arg c: 123 Xtra arg d: poe >>> >>> dictVarArgs('one', d=10, e='zoo', men=('freud', 'gaudi')) formal arg1: one formal arg2: defaultB Xtra arg men: ('freud', 'gaudi') Xtra arg d: 10 Xtra arg e: zoo


Both keyword and non-keyword variable arguments may be used in the same function as long as the keyword dictionary is last and is preceded by the non-keyword tuple, as in the following example:

def newfoo(arg1, arg2, *nkw, **kw):     display regular args and all variable args'     print 'arg1 is:', arg1     print 'arg2 is:', arg2     for eachNKW in nkw:         print 'additional non-keyword arg:', eachNKW     for eachKW in kw.keys():         print "additional keyword arg '%s': %s" % \             (eachKW, kw[eachKW])


Calling our function within the interpreter, we get the following output:

>>> newfoo('wolf', 3, 'projects', freud=90, gamble=96) arg1 is: wolf arg2 is: 3 additional non-keyword arg: projects additional keyword arg 'freud': 90 additional keyword arg 'gamble': 96


11.6.3. Calling Functions with Variable Argument Objects

Above in Section 11.2.4, we introduced the use of * and ** to specify sets of arguments in a function call. Here we will show you more examples of that syntax, with a slight bias toward functions accepting variable arguments.

We will now use our friend newfoo(), defined in the previous section, to test the new calling syntax. Our first call to newfoo() will use the old-style method of listing all arguments individually, even the variable arguments that follow all the formal arguments:

>>> newfoo(10, 20, 30, 40, foo=50, bar=60) arg1 is: 10 arg2 is: 20 additional non-keyword arg: 30 additional non-keyword arg: 40 additional keyword arg 'foo': 50 additional keyword arg 'bar': 60


We will now make a similar call; however, instead of listing the variable arguments individually, we will put the non-keyword arguments in a tuple and the keyword arguments in a dictionary to make the call:

>>> newfoo(2, 4, *(6, 8), **{'foo': 10, 'bar': 12}) arg1 is: 2 arg2 is: 4 additional non-keyword arg: 6 additional non-keyword arg: 8 additional keyword arg 'foo': 10 additional keyword arg 'bar': 12


Finally, we will make another call but build our tuple and dictionary outside of the function invocation:

>>> aTuple = (6, 7, 8) >>> aDict = {'z': 9} >>> newfoo(1, 2, 3, x=4, y=5, *aTuple, **aDict)  arg1 is: 1 arg2 is: 2 additional non-keyword arg: 3 additional non-keyword arg: 6 additional non-keyword arg: 7 additional non-keyword arg: 8 additional keyword arg 'z': 9 additional keyword arg 'x': 4 additional keyword arg 'y': 5


Notice how our tuple and dictionary arguments make only a subset of the final tuple and dictionary received within the function call. The additional non-keyword value '3' and keyword pairs for 'x' and 'y' were also included in the final argument lists even though they were not part of the '*' and '**' variable argument parameters.

Prior to 1.6, variable objects could only be passed to apply() with the function object for invocation. This current calling syntax effectively obsoletes the use of apply(). Below is an example of using these symbols to call any function object with any type of parameter set.

Functional Programming Example

Another useful application of functional programming comes in terms of debugging or performance measurement. You are working on functions that need to be fully tested or run through regressions every night, or that need to be timed over many iterations for potential improvements. All you need to do is to create a diagnostic function that sets up the test environment, then calls the function in question. Because this system should be flexible, you want to allow the testee function to be passed in as an argument. So a pair of such functions, timeit() and testit(), would probably be useful to the software developer today.

We will now present the source code to one such example of a testit() function (see Example 11.5). We will leave a timeit() function as an exercise for the reader (see Exercise 11-12).

This module provides an execution test environment for functions. The testit() function takes a function and arguments, then invokes that function with the given arguments under the watch of an exception handler. If the function completes successfully, a true return value packaged with the return value of the function is sent back to the caller. Any failure causes False to be returned along with the reason for the exception. (Exception is the root class for all runtime exceptions; review Chapter 10 for details.)

Example 11.5. Testing Functions (testit.py)

testit() invokes a given function with its arguments, returning TRue packaged with the return value of the function on success or False with the cause of failure.

1 #!/usr/bin/env python 2 3 def testit(func, *nkwargs, **kwargs): 4 5     try: 6         retval = func(*nkwargs, **kwargs) 7         result = (True, retval) 8     except Exception, diag: 9         result = (False, str(diag)) 10    return result 11 12 def test(): 13     funcs = (int, long, float) 14     vals = (1234, 12.34, '1234', '12.34') 15 16     for eachFunc in funcs: 17         print '-' * 20 18         for eachVal in vals: 19             retval = testit(eachFunc, 20                             eachVal) 21             if retval[0]: 22                 print '%s(%s) =' % \ 23     (eachFunc.__name__, `eachVal`), retval[1] 24             else: 25                print '%s(%s) = FAILED:' % \ 26     (eachFunc.__name__, `eachVal`), retval[1] 27 28 if __name__ == '__main__': 29     test()

The unit tester function test() runs a set of numeric conversion functions on an input set of four numbers. There are two failure cases in this test set to confirm such functionality. Here is the output of running the script:

$ testit.py -------------------- int(1234) = 1234 int(12.34) = 12 int('1234') = 1234 int('12.34') = FAILED: invalid literal for int(): 12.34 -------------------- long(1234) = 1234L long(12.34) = 12L long('1234') = 1234L long('12.34') = FAILED: invalid literal for long(): 12.34 -------------------- float(1234) = 1234.0 float(12.34) = 12.34 float('1234') = 1234.0 float('12.34') = 12.34




Core Python Programming
Core Python Programming (2nd Edition)
ISBN: 0132269937
EAN: 2147483647
Year: 2004
Pages: 334
Authors: Wesley J Chun

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net