13. Subroutines, procedures, functions and definitions

Code is not written in a single continuous long file. Code is broken up into blocks of length of about 5 lines to 2 pages (longer blocks are too big to keep in your head). Depending on the language, these blocks of code are called functions, procedures or subroutines. Python calls them functions. Functions can be kept in their own file or with other files. Functions in one file can be called by import'ing them from another file.

Note

when I learned computing they were called "subroutines". The language C later differentiated two types of subroutines; procedures and functions and I never adopted the nomenclature (only a pendant would notice the difference - a couple of strokes of the keyboard and you convert one to the other). Python uses the word "function".

I work in what was once called a supercomputer center (when it was fashionable to use such a term). The term used there is subroutine. You can use whatever term you like. Python people will expect the term "function".

13.1. Function example - greeting.py: no functions, greeting.py

Here's a program that has no functions. It gives a greeting. Call it greeting.py

#! /usr/bin/python
""" greeting.py
    greeting without any functions
"""

#---------
#main()

print "hello user"

# greeting.py ---------------------------------

The code includes documentation and white space to separate the logical sections (in this case, the documentation and the code). The dividing line and "#main" are to let people know where the code starts executing. This is optional, but some delineator is needed to help you (and others) read your code later. Remember if you're being paid to write code, or work in a group, others will not be impressed if they have to struggle to read it.

Modify this code to say "hello, this code was written by (your name)" [75]

13.2. Function example - greeting_2.py: one function, no parameters

If we want to call the greeting from several places in the code, we turn the printing of the greeting into a function. Call this file greeting_2.py

#! /usr/bin/python
""" greeting_2.py 
    greeting as a function
"""

#------
#functions

def print_greeting():
	"""prints greeting
	"""

	print "hello user"

# print_greeting---------

#main()

#call the function
print_greeting()

# greeting_2.py ---------------------------------

What's going on:

  • a function is declared by the tuple

    def function_name (parameter_list):

    Here the parameter list is empty.

    Note
    tuple: from the word "multiple". Any grouping of one or more factors, variables, parameters that are constant in number in a particular situation. The tuple for a function declaration is the 3 strings above. The tuple for the contents of a PB&J sandwich is "peanut butter, jelly". A PB&J sandwich never has a tuple of 1 or a tuple of 3. In English a tuple of 2 is a double. You can get through computing without ever having to use the word "tuple" (I don't use it) but others use it, so you have to know what it means.
  • indenting:

    the definition of the function (the lines of code that does the work) is indented. See the next section for problems with python indenting.

  • the name of function is added in a comment at the end of the function at the indent level of the string def. This lets you know where the function ends. If you're looking at the tail end of a long function, you will forget which function you're working on. This is optional (as far as the language is concerned), but will save you grief later when scanning your code.
  • The function is called from main() with a list of parameters that match the definition of the function (here empty).
    Note
    nomenclature: main()calls the function. When the function has finished executing, it returns (execution returns to the line following the call).
  • Use white space and empty lines to make your code readable.
  • The comments "#functions" and "#main()" are to help the reader.

13.3. Problems with python indenting

Indenting is a common coding practice to show the reader logical separation of code from the surrounding code. In all languages, except for python, the indenting is only for the reader and is ignored by the compiler/interpreter. In other languages, pairs of "{}" (braces, squigglies) are used to delineate blocks of code; pieces of code that have their own scope (variables created in a block don't exist after the block exits).

Python uses the indenting to show both the start/end of functions (and blocks), and for the reader to show logical sections of code. Proper indenting is required for the code to run, and is not just for clarity to the reader. The Style Guide for Python Code (http://www.python.org/dev/peps/pep-0008/) requires 4 spaces for python indenting. For compatibility with old code, python will accept a tab or 8 spaces (but not mixtures of a tab in one line and 4 spaces in the next).

The problem with python indenting is that python has syntax errors which are silent to inspection by eye (you can have mixtures of tabs and spaces which are the same on consecutive lines, but all you'll get from python is "syntax error" on the first non-white space character). If code has an error in it, you should be able to see it. (This sort of feature is called "broken by design".) This is a rocket waiting to blow up.

There are many style of indenting for readability and code nesting with curly braces Indent style (http://en.wikipedia.org/wiki/Indent_style). Each style has its adherants, and without any tests as to the best style, or any interest in running such tests, claims of superiority of any method are specious and cast doubts on the credibility of the claimant. There is no doubt that people can best read the style they're used to (just like they can best use the keyboard layout they're used to). People working in a team will usually be required to use the style that the leader is most familiar with.

It turns out that (without knowing it) I adopted the Allman style for curly braces, using a tab (1 key stroke) for indenting. I will be using a tab for indenting in the code here. You can use whatever your python interpreter will accept. Be aware that a tab when mouse swiped in X, will appear as 8 spaces in the target code (after a mouse swipe, I have my editor replace all occurences of 8 spaces with a tab).

To help you read the code, you adopt an indenting style so that the indenting level indicates the block level. The compiler doesn't care if the indents and blocks don't match, the compiler looks at the block level. If your indenting and blocks don't match, you'll may have logical control problems (the program will do something, but it may not do what you think it should do). Some editors match pairs of "{}" so that you can check your blocks. In python, indenting is blocking. Editors don't show matching indents and until you're used to the python indenting, you'll run into apparently intractable problems. Here's an example:

#!/usr/bin/python

output_string = ""
sum = 0
for i in range(0,10):
        output_string += str(i)
        for j in range(0,i):
                sum += j*j

        #lots of lines of code

                #including multiple levels of endenting

                        #like these lines





         print output_string

Here's the output

# ./indent.py
  File "./indent.py", line 18
    print output_string
                      ^
IndentationError: unindent does not match any outer indentation level

The problem is that the line "print output_string" is a tab followed by a space (2 indents) instead of a single tab.

Despite the advice of the python people, I think it's safer to use tabs for code indenting

  • it's one keystroke (it's simpler)
  • you can search for them with your editor, and replace any error causing tab-space combinations with a tab.

If your code is nested to great depth (not a great idea), deeply nested code will be diplaced to the right across the page.

13.4. Separation of function declaration and function definition: order of calls to functions

In python, function declaration (the tuple of def name (parameters):) and definition (the lines of code that do the work) occur together. Functions often call other functions. If this second function has not yet been declared, the language/interpreter/compiler doesn't know what to do with the name.

Compare these two programs.

#functions declared in order
def fn1:

def fn2:
	call fn1 #fn1 already declared, no problems

#main()
	fn2
#---------

#functions declared out of order
def fn1:	
	call fn2 #don't know fn2, will have to look for it

def fn2:

#main()
	fn1
#---------

All languages have mechanisms for handling calls to functions that aren't known. You can list all your functions in any order you like (alphabetical by name, the order you wrote them, grouped by functionality), at the top of the file (the usual place) or at the bottom of the file (some people do this).

In many languages, particularly those designed to write code with thousands of files in a large application, the code writer is required to declare functions in a separate header file, which is read before the code file is read, so that all functions are declared before any code is looked at. Separating declaration from definition allows the compiler/interpreter to process files singly. This helps when you're working on one file in a large project.

Interpreted languages (like python, perl) keep track of the function calls they don't know about and exhaustively search all files in their pervue - it will search the Module Search Path. (http://docs.python.org/tut/node8.html#SECTION008110000000000000000) - in the hopes of finding the declaration. This can take a while, but what the heck, it makes life easier for you (let the computer work hard - that's it's job - it's not your job to make life easier for the interpreter, it's the interpreter's job to make life easy for you).

However for small projects, worrying about this sort of thing is a hill of beans. You're probably looking at the screen for 90% of the time trying to figure something out and you don't care if the computer is scanning files for an extra few seconds. Some people have all their functions at the bottom of the file, so the interpreter collects many calls to unknown functions before it gets to the function declarations below. These people are just as productive as the people who have their function declarations in order at the top of their files.

13.5. Function example - greeting_3.py: one function, one parameter

Usually a function needs information from the calling code. The information is passed as parameter(s).

Here we're also going to get input from the user. (Getting data directly from the user is not easy. It's simpler to get it from a file. Here we're going to get data from the user.)

Note

Let's say we want to get the user's name and print it on the screen. Getting input from users is fraught with problems and makes life difficult for the programmer. See Conversing with the user (http://www.freenetpages.co.uk/hp/alan.gauld/tutinput.htm) and Saving Users From Themselves (http://linuxgazette.net/issue83/evans.html).

  • they'll enter "y" or "n" or their phone number.
  • they can enter a string which when printed to your screen will erase your hard disk, so you have to check that the the string they enter is safe (this is a lot of work).
  • It's a waste of a computer to have it talk to one person. You should enter the person's name into a file with thousands of other names and let the computer process the data for everyone.

Since you're doing this for the class and it's your own computer, let's assume you're a benign user who can reply with their name when asked for it.

Try this as greeting_3.py

#! /usr/bin/python 
""" greeting_3 
    greeting as a function
"""

#------
#functions

def print_greeting(user_name):
	"""prints greeting
	"""
	print "hello %s, nice to meet you" % user_name

# print_greeting---------

#main()

#get input
resp = raw_input ("What is your name? ")

#call the function
print_greeting(resp)

# greeting_3.py ---------------------------------

In main() the parameter passed is resp (a string). In print_greeting(), the parameter arrives as user_name. What's going on? In main() the instruction print_greeting(resp) pushes the value of resp onto the parameter stack; the instruction doesn't push the name resp onto the parameter stack. When the function print_greeting() runs, the command print_greeting(user_name) says "assign the value of the first parameter to the variable user_name". At this stage the function doesn't know whether the parameter is a string, int or real (the data in the parameter can only be reasonably treated as a string). You have to tell the function that the parameter is a string, which you do in the first instruction that uses user_name (the print statement).

In greeting_3.py

  • The name for the parameter in the calling code (resp) has some relevance to the surrounding code.
  • the name for the parameter in the calling code (resp) bears no relation to the name of the parameter (user_name) in the function.
  • The name for the parameter in the function is usually some generic name that will work for any situation that the function could be called upon to work in.
  • The two names could be identical, they could be quite different.
  • user_name is not known by the calling code (or any other functions)
  • resp is visible to print_greeting

13.6. Scope - greeting_4.py

Scope is the range in which a variable can be referenced. In programming languages,

  • all variables declared in main() will be visible in any functions that are called by main()
  • the variables declared inside a function will not be visible to other functions or to main().

Let's look at this modified version of greeting_3.py. Call it greeting_4.py. It has extra print statements (to debug the code).

Note
Since adding extra print statements to code fills the screen with output, it's helpful to prepend the name of the routine/function to each output so you can decipher the extra output. When you're done debugging the code, you will first comment out and then later delete these extra lines.
#! /usr/bin/python 
""" greeting_4 
    greeting as a function
"""
#-----------------
#functions

def print_greeting(user_name):
	"""prints greeting
	"""
	print "hello %s, nice to meet you" % user_name
	print "print_greeting: the value of resp is " + resp

# print_greeting--------------

#main()

#get input
resp = raw_input ("What is your name? ")
#call the function
print_greeting(resp)
print "main: the value of user_name is " + user_name

# greeting_4.py ---------------------------------

Variables are only known/visible to functions at levels above in the calling hierachy. Variables are not visible to functions at the same level in the calling hierachy. (The calling level of functions is also described as nesting. Outside the function, variables are known only to nested layers above.)

  • user_name is declared in print_greeting(). It is only visible in print_greeting()
  • resp is declared in main(). It is visible in both main() and in print_greeting()

Scope allows people to write functions independantly of each other knowing that only main() will be able to see the contents of their function. A variable user_name could be used in several functions, and each user_name could have different values.

Because of scope

  • the only connection between a function and the calling code is the function's name, the parameters that are passed to it and the value(s) that it returns.
  • any variable names and values within the routine/function are not visible to any other code.
  • the routine/function will see variables declared by calling code.

While you don't have to know about variable names used by other function, you do have to know names in the code at levels above (which call your routine/function).

In greeting_4() there are two variables

  • resp: Where is it declared, where is it visible: In main(), in print_greeting()[76] ?
  • user_name: Where is it declared, where is it visible: In main(), in print_greeting()[77] ?

The program has instructions to output the value of both variables in each of the two functions main() and print_greeting(). Predict the output of the running the program. Then run the code and explain the output from the two extra print statements [78] .

Note
End Lesson 10

13.7. Code execution: Global and function namespace

The namespace of the code that starts execution is called "global namespace". When you pass a file to the python interpreter, python starts executing on the first instruction it finds in global namespace, i.e. the first code that isn't a function. In other languages, execution starts at a function called main(), which has the top level (global) name space and which calls all other functions. For these lessons, in each file which has function(s), I've put a comment #main() in the place where the python interpreter starts executing. In code thus far, both python and other languages would start executing at the same place. However in a later section on making a module, we will see how python's assumption, about where to start executing, gets us into trouble.

Variables are declared in a namespace: if you declare a variable user_name in a function print_greeting(), then user_name exists in the print_greeting() namespace.

Any function can change the values of a global variable (including functions that haven't been written yet). If none of your functions change any of the global variables, you can't guarantee that someone else, at some later time, won't write one that does. Or you could blunder and write one that changes a global variable without realising it.

Note
In some other languages, any function can read and write global variables; in others you declare a global variable to be read-only by declaring it to be const (constant). In python, global variables are read-only. If you want to write to a global variable (i.e. change it), you have to explicitely declare that you're going to do so inside the function. This "are you sure?" step, requires you to think twice about doing it, but doesn't stop you from doing it. (Thanks to Rick Zantow for his explanation at global variables http://www.velocityreviews.com/forums/t354870-unboundlocalerror-local-variable-colorindex-referenced.html)

Global variables are regarded as sign of poor (or unsafe) programming style. If you use a writeable global variable, expect someone to ask you to justify doing so and to justify not using some safer programming practice. In later demonstration code, you will see global variables being used: usually the safer programming practice would add complexity, obscuring the point of the demonstration code. If you're programming rocket guidance systems, air traffic control or heart monitors, you won't be using any global variables.

13.8. Function example - volume_sphere(): function returns a result

As well as requiring data from the calling routine (passed as parameters), functions are often written to generate a result that's returned to the calling routine.

Call this file volume_sphere.py.

#! /usr/bin/python
""" volume_sphere.py
    returns volume of sphere
"""

#---------------
#functions

def volume_sphere(r):
	pi=3.14159
	result=(4/3)*pi*r*r*r
	return result

# volume_sphere---------------

#main()

#get input
resp = raw_input ("What is the radius of your sphere? ")

#call the function
volume=volume_sphere(resp)	#assign the result of the function call to volume

#formatted output
print "the volume of your sphere is %f" % volume
#this gives the same output
#print "the volume of your sphere is " + repr(volume)

# volume_sphere.py ---------------------

Let's walk through this line by line

#get input
resp = raw_input ("What is the radius of your sphere? ")

You'll be prompted for a number, which you'll enter on the keyboard. Your entry will be assigned to resp

#call the function
volume=volume_sphere(resp)	#assign the result of the function call to volume

When there's several things to do on a line, the parser starts from the right hand side. The parser has a value for resp so next looks at volume_sphere() which, because of the (), is recognised as a call to a function. The interpreter finds volume_sphere and passes resp to volume_sphere(). Note: the interpreter doesn't look at volume=, at least yet. Execution now passes to volume_sphere()

	pi=3.14159

the variable π is assigned the value 3.14159.

	result=(4/3)*pi*r*r*r

the value of (4/3 pi*r3) is assigned to result. Why didn't we just splice the value of π into the formula for the volume of the sphere?

  • It makes the code more readable. Everyone recognises "(4/3)*pi*r3", but you won't neccessarily recognise a formula with lots of numbers in it.
  • Having all the constants at the top of the routine helps the reader know what the routine needs to know.
  • Having constants at the top of the routine, allows you to change them without having your editor traipsing through tested code, where by some clumsy editing, you might unintentionally change something else, and not realise it. Very few people rerun their tests when they make simple changes like this. Even if you do, your tests may not be exhaustive enough to pick up an editing disaster. Don't tempt disaster by re-editing working code when you don't intend to exhaustively retest it.

(We'll shortly replace the line pi=3.14159; stay tuned.)

Here's the new part for this lesson.

	return result

The function pushes result onto the stack and exits. (You can only return one item. If you want to return two numbers, you could return one list containing two numbers.) Execution returns to main() where the interpreter now sees

	volume=result

volume is assigned the value of result

Run the program. Did you get a run-time error about non-ints? An error at run-time means that the code syntax was correct and the program started to run, but you asked the program to do something it can't do. Writing code is full of situations like this; it's a coder's life.. You have to figure your way out of them all the time.

The error message you get here is misleading. The problem is with resp; is it a number? If not, what is it [79] ? You sent the volume_sphere() a string and asked it to use a string as a number, which it can't do, so the program crashed.

The problem with resp is fixed by turning the string contained in resp into a real (floating point) number. All languages have ways of interconverting numbers and strings and they all look much the same. Here's how it's done in python.

number=float(string)
Note

Strong Typing and Weak Typing.

Strongly typed languages will not allow you to send incompatible data types to a function. They check data types before the code is ever allowed to run. Strongly typed languages require the declaration of the function to include the type of any result and the type of each parameter. The above code in a strongly typed language would fail at compile/interpret time and you'd get an error message about mismatched parameter types ("resp" is a string and can't be passed to "volume_sphere" which requires a real). The development time for a strongly typed language is longer, but there will be less chance of errors at run time.

Python is a weakly typed language. Weak typing allows you to quickly write short pieces of code without the relatively large overhead of writing header files and explicitely typing (declaring) parameters. For small programs in a strongly typed language like C or C++, the overhead of writing header files can be daunting,

Weak typing also allows you to write sloppy code, which passes syntax checking, but which crashes at run-time. So you don't use python for rocket guidance, air traffic control or heart monitors. You do use it for short programs, for programs that will only be run a few times and for programs where errors will not be catastrophic and where someone will be on hand to fix them when they do cause a crash.

Both strongly typed languages with a long development time and low error rate, and weakly typed languages with a short development time and high error rate have their place. A bank chooses a bullet proof armoured wagon to transport its money and you choose a paper bag to transport your groceries.

  • Demonstrate the function float() at the python prompt. Feed it a fews strings (strings are delimited by quotes) representing numbers e.g. "3.14159", "98.4". What do you think float() will do if you feed it a number (e.g. 3.14159) (no quotes) [80]

  • Where in your program should you turn the string into a quote? You could turn "resp" into a number in the calling code, or you could turn "r" into a number in the function. Show how you would do both. Think about which is the best solution and justify your choice [81] .

You can fix the code however you want. Here's my fixed code.

#! /usr/bin/python
""" volume_sphere.py
    returns volume of sphere
"""

#---------------
#functions

def volume_sphere(r):
	r=float(r)      #allow function to accept both strings and numbers
	pi=3.14159
	result=(4/3)*pi*r*r*r
	return result

# volume_sphere---------------

#main()

#get input
resp = raw_input ("What is the radius of your sphere? ")
#resp=float(resp)       #don't turn resp from a string into a float here

#call the function, passing a string
volume=volume_sphere(resp)

#formatted output
print "the volume of your sphere is %f" % volume

# volume_sphere.py ---------------------

Run this program with a radius of 1 (which gives me the result 3.141590). Now you have to check that your result is OK. Try with bc.

echo "(4/3)*3.14159" | bc
3.14159

It's the same answer as the python program. Does the answer look OK? If you're not sure, do it by hand (give PI a value of 3 just to get a rough estimate) [82] .

So what went wrong? In case you've forgotten: (4/3) is an integer operation which gives a result of 1. The volume of a sphere of radius 1 is 4.188 (and not 3.14159 as found by the program). To invoke real number operations with bc, you need the math libraries, invoked with bc -l. To stop yourself falling into a trap, always invoke bc with the -l option.

echo "(4/3)*3.14159" | bc -l
4.18878666666666666665
echo "(4.0/3.0)*3.14159" | bc -l
4.18878666666666666665

Here's the fix

	result=(4.0/3.0)*pi*r*r*r

13.9. Checking Code

  • You must check/test every piece of code you ever write; not the whole code as one piece, but check the code line-by-line. (Print out every variable on the way through; then comment those lines when they prove OK; then later remove these commented lines).
  • Include at least one check by hand, using simple numbers that you can do in your head. Don't rely on computers to check computers: all code uses the same libraries and makes the same assumptions. A badly written piece of code could give the same result in many different computer languages (4/3 is 1 in all computer languages).

    When the Space Shuttle flies, it has 4 identical computers running, checking all parameters. If one of the computers gets a different result, the assumption is that there's a hardware failure in that computer and it is shutdown. The 2nd assumption is that the code is perfect and there never will be a software failure. What if the same bug is running in all 4 computers? What you really want is 4 independant (not identical) computers: each computer having different hardware designed and built by separate teams, running a different OS, running software written by 4 independant teams in different languages. Everyone knows the problems with accepting this 2nd assumption, but no-one is prepared to pay for the cost of 4 independant computers.

    There are many contenders for the worst software failures in history. Here's Simpson Garfkinkels top ten History's Worst Software Bugs (http://www.wired.com/software/coolapps/news/2005/11/69355).

    My favourite is a rocket that blew up because a line of code had a ',' rather than a '.'. This line of code was never exercised in tests. During flight, that line of code ran and the program crashed, causing the destruction of the rocket.

13.10. Using Math Libraries

It's not a good idea to type in the value of PI: you could make a mistake. Any language that does math, has the value for PI (and other useful numbers) built in. You don't need to remember exactly how to include PI. Instead look up the documentation or look in google for a piece of working code (google for "python math pi") - I found CGNS Python modules -- Introduction Python" (http://cgns-python.berlios.de/Ipython.html).

There's a couple of places you can put the import statement.

  • at the top of the file, before the function definitions
  • in the function after the documentation
  • at the beginning of main()

All of these will work. However functions are designed to be easy to move/copy to other code. What will happen to your function if you put the import math statement at the beginning of the file or at the beginning of main() and you copy the function to another python file, by swiping it with your mouse [83] ?

Modify your code to use the value of PI from the python math module.

13.11. Function Documentation

Here's what good documentation for a function looks like. If ever you hope to code with other people, or to remember in 6 months what your code does, you will need to do something like this for every function/piece of code you write.

#! /usr/bin/python
""" volume_sphere.py
    returns volume of sphere
"""

#---------------
#functions

def volume_sphere(r):
	"""name      - volume_sphere(r)
	   version   - 1.0 Feb 2008
	   method    - volume =(4.0/3.0)*pi*radius^3
	   parameter - radius as string, integer or real, valid all +/-ve numbers
	   returns   - volume of sphere, float
	   Author    - Homer Simpson, Homer@simpson.com (C) 2008
	   License   - GPL v3.
	"""

	import math     #import the whole math module (including lots of stuff you don't need)
	r=float(r)      #convert string input to float.
	                #this allows the function to accept either a string or a number

	                #pi in module math is called math.pi
	result=(4.0/3.0)*math.pi*r*r*r
	return result

# volume_sphere---------------

#main()

#get input
resp = raw_input ("What is the radius of your sphere? ")

#call the function, passing a string
volume=volume_sphere(resp)

#formatted output
print "the volume of your sphere is %f" % volume

# volume_sphere.py ---------------------

Add appropriate documentation to your version of this function showing

  • name of routine (parameter(s))
  • description of parameter(s) and valid input types and ranges
  • description of result (if there is one)
  • author, contact information, date, copyright (if you want to claim it, optional but good idea)
  • license (optional, but good idea)

    The license dictates the terms under which other people can use the code. The Gnu Public License (in my opinion) is the best license for releasing code: You retain copyright, anyone else can use you code for anything they like. If they in turn release code based on your code, they must also release the source code (including your code, or the modified version of your code). The GPL is designed so that any improvements to freely released code is also freely released.

13.12. Return Value

It's easy to disguise where a function returns. Functions can have many conditional statements, any of which (depending on the flow of the program) can give the return value. Code is allowed to return from anywhere in the function, but doing this makes it hard to read. Badly written code looks like this.

def my_function (param1, param2...)
	if (some complicated condition)
		.
		.
		return (some complicated expression)
	elif (some complicated condition)
		.
		.
		return (some complicated expression)
	elif (some complicated condition)
		.
		.
		return (some complicated expression)
	else
		return (some complicated expression)
	endif

It can be hard to debug this sort of code: it's hard to find all the return statements. Instead there must (should) only be one return statement; the last statement in the function. All output should be put in some obviously named variable (like "result") which will be the argument to the return statement. The code you should write is this

def my_function (param1, param2...)
	if (some complicated condition)
		.
		.
		result = (some complicated expression)
	elif (some complicated condition)
		.
		.
		result = (some complicated expression)
	elif (some complicated condition)
		.
		.
		result = (some complicated expression)
	else
		result = (some complicated expression)
	endif

	return result

This is still hard to read, but you've done your best (multiple conditional statements aren't easy to read). If you're looking for the return values, you can find the string "result" with your editor,

13.13. Function properties

procedure/subroutine/functions have the following properties

  • they're a logical block that can be described in a few words (e.g. gets input data, checks data, sorts data, output results)
  • it's a piece of code that's called twice (or more) from other code (never duplicate a block of code, always make a function out of it).
  • it's well tested and can be put away in a separate block and the internals of how it works can be forgotten about.
  • the interface with the calling code is well defined e.g.volume_sphere.py accepts a string (or number) and returns a number.
  • the implementation (the code that does the work) can be rewritten without any change in function/behaviour/output as seen by the calling program. e.g. there are many types of sorting routines. If your subroutine does a sort, then should be able to replace the code with a different sort routine without making any changes to the calling code.
Note
End Lesson 11