Defining functions - Python in easy steps (2014)

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.

image

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.

image

Avoid using global variables in order to prevent accidental conflict - use only local variables where possible.

imageStart a new Python script by initalizing a global variable

global_var = 1

image

scope.py

imageNext, create a function named “my_vars” to display the value contained within the global variable

def my_vars() :

print( ‘Global Variable:’ , global_var )

imageNow, 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 )

imageThen, add indented statements to the function block to create a coerced global variable and assign an initial value

global inner_var

inner_var = 3

imageAdd a statement after the function to call upon that function to execute the statements it contains

my_vars()

imageFinally, add a statement to display the value contained in the coerced global variable

print( ‘Coerced Global:’ , inner_var )

imageSave 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

image

image

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 )

image

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’ )

image

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.

imageStart 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 )

image

args.py

imageNext, call the function passing string values to the function arguments in the order they appear

echo( ‘Mike’ , ‘Python’ , ‘Windows’ )

imageNow, call the function passing string values to the function arguments by specifying the argument names

echo( lang = ‘Python’ , sys = ‘Mac OS’ , user = ‘Anne’ )

imageThen, 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 )

imageFinally, add statements to call the second function both using and overriding default values

mirror()

mirror( lang = ‘Java’ )

mirror( user = ‘Tony’ )

mirror( ‘Susan’ , ‘C++’ )

imageSave 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

image

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

image

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.

imageStart a new Python script by initializing a variable with user input of an integer value for manipulation

num = input( ‘Enter An Integer:’ )

image

return.py

imageNext, add a function definition that accepts a single argument value to be passed from the caller

def square( num ) :

imageNow, 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’

imageThen, 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

imageFinally, add a statement to output a string and the returned value from the function call

print( num , ‘Squared Is:’ , square( num ) )

imageSave 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

image

image

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.

image

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 ]

imageStart 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

image

lambda.py

imageNext, 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 ]

imageNow, 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 ) )

imageThen, 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 ]

imageFinally, 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 ) )

imageSave 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

image

image

Function definitions that contain just one statement can be written on just one line - as seen here.

image

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.

imageStart 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.

image

incomplete.py

imageSave the file in your scripts directory then open a Command Prompt window there and run this program - to see the interpreter report an error

image

imageReplace 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

image

image

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.

imageStart a new Python script by initializing a variable with a string value

title = ‘\nPython In Easy Steps\n’

image

skip.py

imageNext, add a loop to print each character of the string

for char in title : print( char , end = ‘ ‘ )

imageNow, 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 = ‘ ‘ )

imageFinally, 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 = ‘ ‘ )

imageSave 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

image

image

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.

image

Changing the conditional test in this loop to read while i < 3 will cause a StopIteration error when called for the third time.

imageStart 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

image

yield.py

imageNext, 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

imageNow, assign the returned generator object to a variable

fib = fibonacci_generator()

imageFinally, 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 )

imageSave 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

image

image

Here, the variables are initialized with a common value in a single statement.

image

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.

image

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.

imageStart a new Python script by initializing a variable with a string value

title = ‘Python In Easy Steps’

image

try.py

imageNext, add a try statement block that attempts to display the variable value - but specifies the name incorrectly

try :

print( titel )

imageNow, add an except statement block to display an error message when a NameError occurs

except NameError as msg :

print( msg )

imageSave the file in your scripts directory then open a Command Prompt window there and run this program - to see how the error gets handled

image

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.

imageStart a new Python script by initializing a variable with an integer value

day = 32

image

raise.py

imageNext, 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.

imageNow, add an except statement block to display an error message when a ValueError occurs

except ValueError as msg :

print( ‘The Program found An’ , msg )

imageThen, add a finally statement block to display a message after the exception has been handled successfully

finally :

print( ‘But Today Is Beautiful Anyway.’ )

imageSave the file in your scripts directory then open a Command Prompt window there and run this program - to see the raised error get handled

image

image

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

image

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.

image

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.

imageStart a new Python script by initializing a list with several string values

chars = [ ‘Alpha’ , ‘Beta’ , ‘Gamma’ , ‘Delta’ , ‘Epsilon’ ]

image

assert.py

imageNext, define a function to accept a single argument

def display( elem ) :

imageNow, 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 ] )

imageThen, initialize a variable with an integer value and call the function, passing this variable value as its argument

elem = 4

display( elem )

imageFinally, change the variable value then call the function once more, passing the new variable value as its argument

elem = elem / 2

display( elem )

imageSave the file in your scripts directory then open a Command Prompt window there and run this program - to see an AssertionError reported

image

image

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