Python in easy steps (2014)
4
Defining functions
This chapter demonstrates how to create functions that can be called to execute statements when the program requires them.
Understanding scope
Supplying arguments
Returning values
Using callbacks
Adding placeholders
Producing generators
Handling exceptions
Debugging assertions
Summary
Understanding scope
Previous examples in this book have used built-in functions of the Python programming language, such as the print() function. However, most Python programs contain a number of custom functions that can be called as required when the program runs.
Function statements must be indented from the definition line by the same amount so the Python interpreter can recognize the block.
A custom function is created using the def (definition) keyword followed by a name of your choice and ( ) parentheses. The programmer can choose any name for a function except the Python keywords listed on the inside front cover of this book, and the name of an existing built-in function. This line must end with a : colon character, then the statements to be executed whenever the function gets called must appear on lines below and indented. Syntax of a function definition, therefore, looks like this:
def function-name ( ) :
statements-to-be-executed
statements-to-be-executed
Once the function statements have been executed, program flow resumes at the point directly following the function call. This modularity is very useful in Python programming to isolate set routines so they can be called upon repeatedly.
To create custom functions it is necessary to understand the accessibility (“scope”) of variables in a program:
•Variables created outside functions can be referenced by statements inside functions - they have “global” scope
•Variables created inside functions cannot be referenced from outside the function in which they have been created - these have “local” scope
The limited accessibility of local variables means that variables of the same name can appear in different functions without conflict.
If you want to coerce a local variable to make it accessible elsewhere it must first be declared with the Python global keyword followed by its name only. It may subsequently be assigned a value that can be referenced from anywhere in the program. Where a global variable and a local variable have the same name the function will use the local version.
Avoid using global variables in order to prevent accidental conflict - use only local variables where possible.
Start a new Python script by initalizing a global variable
global_var = 1
scope.py
Next, create a function named “my_vars” to display the value contained within the global variable
def my_vars() :
print( ‘Global Variable:’ , global_var )
Now, add indented statements to the function block to initialize a local variable and display the value it contains
local_var = 2
print( ‘Local variable:’ , local_var )
Then, add indented statements to the function block to create a coerced global variable and assign an initial value
global inner_var
inner_var = 3
Add a statement after the function to call upon that function to execute the statements it contains
my_vars()
Finally, add a statement to display the value contained in the coerced global variable
print( ‘Coerced Global:’ , inner_var )
Save the file in your scripts directory then open a Command Prompt window there and run this program - to see the custom function display the variable values
Variables that are not global but appear in some outer scope can be addressed using the nonlocal keyword.
Supplying arguments
When defining a custom function in Python programming you may, optionally, specify an “argument” name between the function’s parentheses. A value can then be passed to that argument by specifying the value in the parentheses of the call to the function. The function can now use that passed in value during its execution by referencing it via the argument name. For example, defining a function to accept an argument to print out like this:
def echo( user ) :
print( ‘User:’ , user )
Argument-naming follows the same conventions as variables and functions.
A call to this function must specify a value to be passed to the argument within its parentheses so it can be printed out:
echo( ‘Mike’ )
Multiple arguments (a.k.a. “parameters”) can be specified in the function definition by including a comma-separated list of argument names within the function parentheses:
def echo( user , lang , sys ) :
print( User:’ , user , ‘Language:’ , lang , ‘Platform:’ , sys )
When calling a function whose definition specifies arguments, the call must include the same number of data values as arguments. For example, to call this example with multiple arguments:
echo( ‘Mike’ , ‘Python’ , ‘Windows’ )
The passed values must appear in the same order as the arguments list unless the caller also specifies the argument names like this:
echo( lang = ‘Python’ , user = ‘Mike’ , sys = ‘Windows’ )
Name arguments the same as variables passed to them to make the data movement obvious.
Optionally, a default value may be specified in the argument list when defining a function. This will be overridden when the caller specifies a value for that argument but will be used by the function when no value gets passed by the caller:
def echo( user , lang , sys = ‘Linux’ ) :
print( User:’ , user , ‘Language:’ , lang , ‘Platform:’ , sys )
This means you may call the function passing fewer values than the number of arguments specified in the function definition, to use the default argument value, or pass the same number of values as specified arguments to override the default value.
Start a new Python script by defining a function to accept three arguments that will print out their passed in values
def echo( user , lang , sys ) :
print( ‘User:’, user, ‘Language:’, lang, ‘Platform:’, sys )
args.py
Next, call the function passing string values to the function arguments in the order they appear
echo( ‘Mike’ , ‘Python’ , ‘Windows’ )
Now, call the function passing string values to the function arguments by specifying the argument names
echo( lang = ‘Python’ , sys = ‘Mac OS’ , user = ‘Anne’ )
Then, define another function to accept two arguments with default values that will print out argument values
def mirror( user = ‘Carole’ , lang = ‘Python’ ) :
print( ‘\nUser:’ , user , ‘Language:’ , lang )
Finally, add statements to call the second function both using and overriding default values
mirror()
mirror( lang = ‘Java’ )
mirror( user = ‘Tony’ )
mirror( ‘Susan’ , ‘C++’ )
Save the file in your scripts directory then open a Command Prompt window there and run this program - to see the function display the argument values
Returning values
Like Python’s built-in str() function, which returns a string representation of the value specified as its argument by the caller, custom functions can also return a value to their caller by using the Python return keyword to specify a value to be returned. For example, to return to the caller the total of adding two specified argument values like this:
def sum( a , b ) :
return a + b
The returned result may be assigned to a variable by the caller for subsequent use by the program like this:
total = sum( 8 , 4 )
print( ‘Eight Plus Four Is:’ , total )
Or the returned result may be used directly “in-line” like this:
print( ‘Eight Plus Four Is:’ , sum( 8 , 4 ) )
Typically, a return statement will appear at the very end of a function block to return the final result of executing all statements contained in that function.
A return statement may, however, appear earlier in the function block to halt execution of all subsequent statements in that block. This immediately resumes execution of the program at the caller. Optionally, the return statement may specify a value to be returned to the caller or the value may be omitted. Where no value is specified, a default value of None is assumed. Typically, this is used to halt execution of the function statements after a conditional test is found to be False. For example, where a passed argument value is below a specified number:
def sum( a , b ) :
if a < 5 :
return
return a + b
You can specify a default value for an argument in the function definition.
In this case, the function will return the default value None when the first passed argument value is below five and the final statement will not be executed.
Where the function is to perform arithmetic, user input can be validated for integer values with the built-in isdigit() function.
Start a new Python script by initializing a variable with user input of an integer value for manipulation
num = input( ‘Enter An Integer:’ )
return.py
Next, add a function definition that accepts a single argument value to be passed from the caller
def square( num ) :
Now, insert into the function block an indented statement to validate the passed value as an integer or halt further execution of the function’s statements
if not num.isdigit() :
return ‘Invalid Entry’
Then, add indented statements to cast the passed value as an int data type then return the sum of squaring that value to the caller
num = int( num )
return num * num
Finally, add a statement to output a string and the returned value from the function call
print( num , ‘Squared Is:’ , square( num ) )
Save the file in your scripts directory then open a Command Prompt window there and run this program - to see the function display the returned values
Remember that user input is read as a str data type - so must be cast into an int or float data type for arithmetic.
Using callbacks
In Python, a named function is created using the def keyword to specify a function name, which can be used to call that function at any time in the program to execute the statements it contains. Optionally, the named function can return a value to the caller.
Python also allows an anonymous un-named function to be created using the lambda keyword. An anonymous function may only contain a single expression which must always return a value.
Unlike the usual creation of a function with the def keyword, the creation of a function with the lambda keyword returns a “function object”. This can be assigned to a variable, which can then be used to reference (“call back”) the function at any time in the program to execute the expression it contains.
The lambda keyword, therefore, offers the programmer an alternative syntax for the creation of a function. For example:
def square( x ) :
return x ** 2
can alternatively be written more succinctly as...
square = lambda x : x ** 2
In either case, the call square(5) returns the result 25 by passing in an integer argument to the function. Note that the lambda keyword is followed by an argument without parentheses and the specified expression does not require the return keyword as all functions created with lambda must implicitly return a value.
In-line lambda callbacks are often used to define the behavior of buttons in a GUI program.
While the lambda keyword offers an alternative way to create a function it is mostly used to embed a function within the code. For instance, callbacks are frequently coded as inline lambda expressions embedded directly in a caller’s arguments list - instead of being defined with the def keyword elsewhere in the program and referenced by name. For example:
def function_1 : statements-to-be-executed
def function_2 : statements-to-be-executed
callbacks = [ function_1 , function_2 ]
can alternatively be written more succinctly as...
callbacks = [ lambda : expression , lambda : expression ]
Start a new Python script by defining three functions to return a passed argument raised to various powers
def function_1( x ) : return x ** 2
def function_2( x ) : return x ** 3
def function_3( x ) : return x ** 4
lambda.py
Next, add a statement to create a list of callbacks to each of the functions by referencing their names
callbacks = [ function_1 , function_2 , function_3 ]
Now, display a heading and the result of passing a value to each of the named functions
print( ‘\nNamed Functions:’ )
for function in callbacks : print( ‘Result:’ , function( 3 ) )
Then, add a statement to create a list of callbacks to inline anonymous functions that return a passed argument raised to various powers
callbacks = \
[ lambda x : x ** 2 , lambda x : x ** 3 , lambda x : x ** 4 ]
Finally, display a heading and the result of passing a value to each of the anonymous functions
print( ‘\nAnonymous Functions:’ )
for function in callbacks : print( ‘Result:’ , function( 3 ) )
Save the file in your scripts directory then open a Command Prompt window there and run this program - to see returns from regular and anonymous functions
Function definitions that contain just one statement can be written on just one line - as seen here.
The \ backslash character can be used to allow code to continue on the next line - as seen here.
Adding placeholders
The Python pass keyword is useful when writing program code as a temporary placeholder that can be inserted into the code at places where further code needs to be added later. The pass keyword is inserted where a statement is required syntactically but it merely performs a “null” operation - when it is executed nothing happens and no code needs to be executed. This allows an incomplete program to be executed for testing by simulating correct syntax so the interpreter does not report errors.
Start a new Python script by initializing a variable with a Boolean value then add an incomplete conditional test
bool = True
if bool :
print( ‘Python In Easy Steps’ )
else :
# Statements to be inserted here.
incomplete.py
Save the file in your scripts directory then open a Command Prompt window there and run this program - to see the interpreter report an error
Replace the comment with the pass keyword then save the file and run the program again - to see the program execute as the interpreter does not now find an error
pass.py
In loop structures it is important not to confuse the pass keyword, which allows the interpreter to process all subsequent statements on that iteration, with the continue keyword, which skips subsequent statements on that iteration of the loop only.
Start a new Python script by initializing a variable with a string value
title = ‘\nPython In Easy Steps\n’
skip.py
Next, add a loop to print each character of the string
for char in title : print( char , end = ‘ ‘ )
Now, add a loop that prints each string character but replaces any ‘y’ character then skips to the next iteration
for char in title :
if char == ‘y’ :
print( ‘*’ , end = ‘ ‘ )
continue
print( char , end = ‘ ‘ )
Finally, add a loop that prints each string character but inserts an asterisk before each ‘y’ character
for char in title :
if char == ‘y’ :
print( ‘*’ , end = ‘ ’ )
pass
print( char , end = ‘ ‘ )
Save the file in your scripts directory then open a Command Prompt window there and run this program - to see a different output from each loop
In a loop the continue keyword continues on the next iteration, whereas the pass keyword passes on to the next statement of the same iteration.
Producing generators
When a Python function is called, it executes the statements it contains and may return any value specified to the return keyword. After the function ends, control returns to the caller and the state of the function is not retained. When the function is next called, it will process its statements from start to finish once more.
A Python generator, on the other hand, is a special function that returns a “generator object” to the caller rather than a data value. This, effectively, retains the state of the function when it was last called so it will continue from that point when next called.
Generator functions are produced by defininition just like regular functions but contain a “yield” statement. This begins with the Python yield keyword and specifies the generator object to be returned to the caller. When the yield statement gets executed, the state of the generator object is frozen and the current value in its “expression list” is retained. The generator object returned by the yield statement can be conveniently assigned to a variable. Python’s built-in next() function can then specify that variable name within its parentheses to continue execution of the function from the point at which it was frozen - exactly as if the yield statement were just another external call.
Repeatedly calling the generator object with the next() function continues execution of the function until it raises an exception. This can be avoided by enclosing the yield statement within an infinite loop so it will return successive values on each iteration. For example, to yield an incremented value on each call:
def incrementer() :
i = 1
while True :
yield i
i += 1
inc = incrementer()
print( next( inc ) )
print( next( inc ) )
print( next( inc ) )
These calls display the integer value 1, then 2, then 3.
Perhaps more usefully, the generator object can be referenced from a loop to successively iterate through values.
Changing the conditional test in this loop to read while i < 3 will cause a StopIteration error when called for the third time.
Start a new Python script by defining a function that begins by initializing two variables with an integer of one
def fibonacci_generator() :
a = b = 1
yield.py
Next, in the function body insert an indented infinite loop to yield the addition of two previous values
while True :
yield a
a , b = b , a + b
Now, assign the returned generator object to a variable
fib = fibonacci_generator()
Finally, add a loop to successively call the generator function and display its value on each iteration
for i in fib :
if i > 100 :
break
else :
print( ‘Generated:’ , i )
Save the file in your scripts directory then open a Command Prompt window there and run this program - to see the loop display increasing generated values
Here, the variables are initialized with a common value in a single statement.
You can use the in-built type() function to confirm the object type - here, type( fib ) is confirmed as a generator class object.
Handling exceptions
Sections of a Python script in which it is possible to anticipate errors, such as those handling user input, can be enclosed in a try except block to handle “exception errors”. The statements to be executed are grouped in a try : block and exceptions are passed to the ensuing except : block for handling. Optionally, this may be followed by a finally : block containing statements to be executed after exceptions have been handled.
Discover more built-in exceptions online at http://docs.python.org/3/library/exceptions.html
Python recognizes many built-in exceptions such as the NameError which occurs when a variable name is not found, the IndexError which occurs when trying to address a non-existent list index, and the ValueError which occurs when a built-in operation or function receives an argument that has an inappropriate value.
Each exception returns a descriptive message that can, usefully, be assigned to a variable with the as keyword. This can then be used to display the nature of the exception when it occurs.
Start a new Python script by initializing a variable with a string value
title = ‘Python In Easy Steps’
try.py
Next, add a try statement block that attempts to display the variable value - but specifies the name incorrectly
try :
print( titel )
Now, add an except statement block to display an error message when a NameError occurs
except NameError as msg :
print( msg )
Save the file in your scripts directory then open a Command Prompt window there and run this program - to see how the error gets handled
Multiple exceptions can be handled by specifying their type as a comma-separated list in parentheses within the except block:
except ( NameError , IndexError ) as msg :
print( msg )
You can also compel the interpreter to report an exception by using the raise keyword to specify the type of exception to be recognized and a custom descriptive message in parentheses.
Start a new Python script by initializing a variable with an integer value
day = 32
raise.py
Next, add a try statement block that tests the variable value then specifies an exception and custom message
try :
if day > 31 :
raise ValueError( ‘Invalid Day Number’ )
# More statements to execute get added here.
Now, add an except statement block to display an error message when a ValueError occurs
except ValueError as msg :
print( ‘The Program found An’ , msg )
Then, add a finally statement block to display a message after the exception has been handled successfully
finally :
print( ‘But Today Is Beautiful Anyway.’ )
Save the file in your scripts directory then open a Command Prompt window there and run this program - to see the raised error get handled
Statements in the try block are all executed unless or until an exception occurs.
Debugging assertions
When tracking down (debugging) errors in your code it is often useful to “comment-out” one or more lines of code by prefixing each line with the # hash character - as used for your comments. The Python interpreter will then omit execution of those lines so helps to localize where a problem lies. For example, where you suspect a variable assignment problem it can be excluded like this:
# elem = elem / 2
If the program now runs without errors the commented-out assignment can be assumed to be problematic.
Another useful debugging technique employs the Python assert keyword to add error-checking code to your script. This examines a specified test expression for a Boolean True or False result and reports an “AssertionError” when the test fails. Optionally, an assert statement can include a descriptive message to supply when reporting an AssertionError, and has this syntax:
assert test-expression , descriptive-message
When the test expression fails, the interpreter reports the AssertionError and halts execution of the script but when the test succeeds, the assert statement does nothing and execution of the script continues.
Employing assert statements is an effective way to document your script as their descriptive messages provide commentary and their tests alert you when your code is erroneous.
You can have the interpreter ignore all assert statements using a -O switch in the run command - for example python -O assert.py.
Assert versus Exception
At first glance an AssertionError can appear confusingly similar to an Exception but it is important to recognize their distinctions:
•Exceptions provide a way to handle errors that may legitimately occur at runtime
•AssertionErrors provide a way to alert the programmer to mistakes during development
Typically, assert statements will be removed from release versions of a program after debugging is complete, whereas except statements will remain to handle runtime errors.
Start a new Python script by initializing a list with several string values
chars = [ ‘Alpha’ , ‘Beta’ , ‘Gamma’ , ‘Delta’ , ‘Epsilon’ ]
assert.py
Next, define a function to accept a single argument
def display( elem ) :
Now, add indented statements in the function body to ensure the passed argument value is an integer then display a list element of that index number
assert type( elem ) is int , ‘Argument Must Be Integer!’
print( ‘List Element’ , elem , ‘=’ , chars[ elem ] )
Then, initialize a variable with an integer value and call the function, passing this variable value as its argument
elem = 4
display( elem )
Finally, change the variable value then call the function once more, passing the new variable value as its argument
elem = elem / 2
display( elem )
Save the file in your scripts directory then open a Command Prompt window there and run this program - to see an AssertionError reported
This AssertionError occurs because the division operation returns a float value, not an integer value.
Summary
•Functions are defined using the def keyword and contain indented statements to execute when the function gets called
•Variables with global scope can be referenced from anywhere, but variables with local scope can only be referenced from within the function in which they are declared
•Arguments are declared as a comma-separated list within the parentheses of a function definition
•Function calls must supply data for each function argument unless a default value is specified in their declaration
•Optionally, a function can include a return statement to return a value to the caller
•An anonymous function containing a single expression is created with the lambda keyword and returns a function object
•Callbacks are frequently coded as inline lambda expressions embedded directly in a caller’s argument list
•Placeholders can be created by inserting the pass keyword where a statement is required syntactically
•A generator function is created when a statement using the yield keyword appears in its function block
•Generator functions retain the state of the function when last called and return a generator object to the caller
•The built-in next() function can be used to continue execution of a generator function from the point where it was frozen
•Anticipated runtime exception errors can be handled by enclosing statements in a try except block
•Optionally, a finally statement can be used to specify statements to be executed after exceptions have been handled
•Error-checking code can be added to scripts using the assert keyword to report development errors